Kem recently reported that there was a serious bug in Currency data type for 64-bit applications. Another member of the forum pointed out to me another bug, even more serious, on Currency (even in 32 bits).
Since this data type is often used in accounting and business management software, it is particularly serious. False data that can be recorded in the accounts.
Are there other problems reported with numbers (double, integer, currency…)?
Please consider adding your points to these Feedback!
Thank you Jean-Yves! I hadn’t seen all those threads.
I’m going to study your workaround, but it’s really disappointing that Xojo inc. doesn’t fix bugs that touch the fundamentals of language. They could at least have added a warning in the documentation if the problem is too complicated/long to resolve.
Like everyone else, I store all my financial values in integers (cents), I also do as many calculations as possible with integers. But sometimes there are intermediate calculations made with currency. I never imagined that a data type would be unreliable!
I’ll study your workaround. And there’s a huge amount of verification work on all my existing codes.
Please, Xojo inc! Problems on the Currency have been reported since at least 2014, many customers have already reported their disappointment to you, please take this into account!
Please Xojo Inc. take the message in this thread highly serious and prioritize these matters above adding new stuff.
We dealt with it far too long.[/quote]
There remains issues with the Text type on Linux as well. I discussed this at length in my 2017 review series. Suffice to say they make minimal effort to test and verify that core data types work as intended. This really hurts the language and tool.
It is difficult to speculate why such fundamental issues persist. Perhaps the signal to noise ratio in the forums is too high. With Norman stepping out I feel it will be even harder to bring attention to critical issues.
Feedback outside of us voting does not seem to have any real prioritization capabilities. I am sure they have some internally but surely from blogs and numerous posts someone over there is aware…
This can be useful for some of you, a class with some “Currency” functionality I wrote few minutes ago. Expand it as necessary.
Barely tested. Please do more tests. The AsInt returns an Int64 with the contents of the “money” for storage purposes, the AsDbl can be used for calculations.
[code]Class Money
Function Add(d As Double) As Money
Dim m As New Money(d)
intVal=intVal+m.AsInt
Return me
End Function
Function Add(m As Money) As Money
intVal=intVal+m.AsInt
Return me
End Function
Sub Constructor(d As Double)
intVal = d * 10000
End Sub
Sub Constructor(m As Money)
intVal = m.AsInt
End Sub
Function equals(d As Double) As Boolean
Dim m As New Money(d)
Return intVal=m.AsInt
End Function
Function equals(m As Money) As Boolean
Return intVal=m.AsInt
End Function
Sub Go()
// Does nothing to end one liners like m.Add(x).Subt(y).Round.Go
End Sub
Function Round() As Money
// 1.3651 -> 1.3700
If intVal Mod 100 > 49 Then intVal = intVal + 100
intVal = (intVal \\ 100) * 100
Return me
End Function
Function Save() As Money
saveVal = intVal
Return me
End Function
Function Restore() As Money
intVal = saveVal
Return me
End Function
Function Subt(d As Double) As Money
Dim m As New Money(d)
intVal=intVal-m.AsInt
Return me
End Function
Function Subt(m As Money) As Money
intVal=intVal-m.AsInt
Return me
End Function
Function Times(d As Double) As Money
intVal=((intVal/10000)*d)*10000
Return me
End Function
Function Times(m As Money) As Money
intVal=((intVal/10000)*m.AsDbl)*10000
Return me
End Function
Function ToString(f As String = "-###,###,###,###,##0.00##") As String
Return Format(intVal / 10000, f)
End Function
Function ToText(f As String = "-###,###,###,###,##0.00##") As Text
Return Format(intVal / 10000, f).ToText
End Function
Function Trunc() As Money
// 1.3699 -> 1.3600
intVal = (intVal \\ 100) * 100
Return me
End Function
Getter AsCurr As Currency
Dim m As New MemoryBlock(8)
m.Int64Value(0)=intVal
Return m.CurrencyValue(0)
End Getter
Setter AsCurr As Currency
Dim m As New MemoryBlock(8)
m.CurrencyValue(0)=Value
intVal = m.Int64Value(0)
End Setter
Getter AsDbl As Double
return intVal / 10000
End Getter
Setter AsDbl As Double
intVal = value * 10000
End Setter
Getter AsInt As Int64
return intVal
End Getter
Setter AsInt As Int64
intVal = value
End Getter
Property Private intVal As Int64 = 0
Property Private saveVal As Int64 = 0
Okay, the reason is technically a problem with code generation:
0x1000c041e <+238>: callq 0x1000c6ea4 ; symbol stub for: RuntimeCvtCurrencyToUInt64
0x1000c0423 <+243>: movq -0x50(%rbp), %rdi
0x1000c0427 <+247>: movq %rax, -0x58(%rbp)
0x1000c042b <+251>: callq 0x1000c6ea4 ; symbol stub for: RuntimeCvtCurrencyToUInt64
0x1000c0430 <+256>: movq -0x58(%rbp), %rcx
0x1000c0434 <+260>: cmpq %rax, %rcx
The function RuntimeCvtCurrencyToUInt64 are called to convert each currency value to UInt64.
you see that RuntimeCvtCurrencyToUInt64 returns only the part before dot, so we compare 1 to 1, which is equal.
with
Dim a As Currency = 3.32
Dim b As Currency = 5.33
I do get
General Purpose Registers:
rax = 0x0000000000000005
rbx = 0x0000000100b49fe0
rcx = 0x0000000000000003
so I think this is right. RuntimeCvtCurrencyToUInt64 should not be called at all in this case and compared directly.
So the bug is that they do UInt64 comparison instead of currency comparison.
[quote=371407:@Rick Araujo]This can be useful for some of you, a class with some “Currency” functionality I wrote few minutes ago. Expand it as necessary.
Barely tested. Please do more tests. The AsInt returns an Int64 with the contents of the “money” for storage purposes, the AsDbl can be used for calculations.[/quote]
dim Value3 as Currency = 46.78
dim Value4 as Currency = 1.509
dim Result2 as Currency = Value3/Value4 = 31.1866
dim Result3 as Currency = 46.78/Value4 = 31.0007
And
[quote]
the calculation is Currency1 / Currency2, then only 2 decimals are used for Currency2
but when the calculation is 46.78 / Currency2, then the 4 decimals in Currency2 are used.[/quote]
For the divine, the first one calls RuntimeDivCurrency, while second one is performed with double numbers and divsd command, so a double division. Could be RuntimeDivCurrency is not working as expected.
Well, it looks like RuntimeDivCurrency does divide the second parameter by 100 and multiplies the first parameter by 100, so your division gets 4678.0 / 1.50 internally.
I would suggest to make a check in the RuntimeDivCurrency:
if dividend is <= 922337203685477.5807 (and > -922337203685477.5807), please multiply dividend by 10000, than do UInt64 division with divisor and be happy.
e.g.
46.78 is internally 467800.
1.509 is internally 15090.
467800 * 10000 / 15090 gives 310006, which is shown as 31.0006
even better may be using *100000 and do rounding.
467800 * 100000 / 15090 gives 3100066, which could be rounded to 31.0007
(if last digit of x is >=5, result is x/10+1, else x/10)
For use with 100000, please use only with numbers < 92233720368547.7580 and > -92233720368547.7580