www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Money type

reply Vitaly Livshic <vitaly_himki mail.ru> writes:
Good day.

I came from Java world, where 'double' type inadequate for money 
calculation. BigDecimal serves for it. This is ugly type, but 
gives precise results.

Which type I must use for money in D?
Jan 01
next sibling parent Steven Schveighoffer <schveiguy gmail.com> writes:
On 1/1/20 10:41 AM, Vitaly Livshic wrote:
 Good day.
 
 I came from Java world, where 'double' type inadequate for money 
 calculation. BigDecimal serves for it. This is ugly type, but gives 
 precise results.
 
 Which type I must use for money in D?
I generally used a fixed-point type. Recently, I was going to use a project in code.dlang.org called fixed, but the string-based constructor uses a conversion to double first, negating the precision of a fixed point type. Another developer is making a new fixed point type here: https://github.com/m3m0ry/fixedpoint But it's not completely ready yet. -Steve
Jan 01
prev sibling next sibling parent reply JN <666total wp.pl> writes:
On Wednesday, 1 January 2020 at 15:41:32 UTC, Vitaly Livshic 
wrote:
 Good day.

 I came from Java world, where 'double' type inadequate for 
 money calculation. BigDecimal serves for it. This is ugly type, 
 but gives precise results.

 Which type I must use for money in D?
There's a BigInt type in the standard library which might work - https://dlang.org/phobos/std_bigint.html
Jan 01
parent reply Vitaly Livshic <vitaly_himki mail.ru> writes:
  Thanks Steve, JN.

  JN, BigInt is big integer. It cannot works with fractional 
numbers. It is sadly, that wide-spread languages have no 
convinient money type. This is abnormal.
  Hope, D will have fixed point type either as built-in type or 
library.
Jan 01
next sibling parent reply IGotD- <nise nise.com> writes:
On Wednesday, 1 January 2020 at 18:16:01 UTC, Vitaly Livshic 
wrote:
  Thanks Steve, JN.

  JN, BigInt is big integer. It cannot works with fractional 
 numbers. It is sadly, that wide-spread languages have no 
 convinient money type. This is abnormal.
  Hope, D will have fixed point type either as built-in type or 
 library.
That's probably because there is no standard how to deal with currencies. Think about Shillings and it goes 12 pence on one Shilling, weird stuff like that. Also why use fixed point? Why not have currency * 100 as there goes hundred cents in one base currency, for many currencies. To convert it back you just divide by 100 and you get the value and the cent value. Well * 100 is fixed point, but in computer land fixed point usually means some scale factor of the power of 2.
Jan 01
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 1/1/20 1:46 PM, IGotD- wrote:
 On Wednesday, 1 January 2020 at 18:16:01 UTC, Vitaly Livshic wrote:
  Thanks Steve, JN.

  JN, BigInt is big integer. It cannot works with fractional numbers. 
 It is sadly, that wide-spread languages have no convinient money type. 
 This is abnormal.
  Hope, D will have fixed point type either as built-in type or library.
That's probably because there is no standard how to deal with currencies. Think about Shillings and it goes 12 pence on one Shilling, weird stuff like that.
Well, that is just odd, as you then need 2 number systems, one with base 12, and one with base 10. Probably best to split that into a byte for pence and some other integer for the shillings.
 
 Also why use fixed point? Why not have currency * 100 as there goes 
 hundred cents in one base currency, for many currencies. To convert it 
 back you just divide by 100 and you get the value and the cent value. 
 Well * 100 is fixed point, but in computer land fixed point usually 
 means some scale factor of the power of 2.
That is what I use. Fixed point with a factor of power of 10. In other words, a fixed point number with 2 decimal places would be sufficient for such currency. When doing math on such types, you just need to deal with the underlying numbers, and it works fine. -Steve
Jan 01
parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= <ola.fosheim.grostad gmail.com> writes:
On Wednesday, 1 January 2020 at 19:01:36 UTC, Steven 
Schveighoffer wrote:
 That is what I use. Fixed point with a factor of power of 10. 
 In other words, a fixed point number with 2 decimal places 
 would be sufficient for such currency. When doing math on such 
 types, you just need to deal with the underlying numbers, and 
 it works fine.
You don't need fixed point, just store cents in 64 bit floating point and you get at least the same accuracy as a 53 bit integer fixed point.
Jan 01
next sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 1/1/20 3:20 PM, Ola Fosheim Grøstad wrote:
 On Wednesday, 1 January 2020 at 19:01:36 UTC, Steven Schveighoffer wrote:
 That is what I use. Fixed point with a factor of power of 10. In other 
 words, a fixed point number with 2 decimal places would be sufficient 
 for such currency. When doing math on such types, you just need to 
 deal with the underlying numbers, and it works fine.
You don't need fixed point, just store cents in 64 bit floating point and you get at least the same accuracy as a 53 bit integer fixed point.
It is stored that way. Stored as a long. Just nicer to deal with printing and such. And instead of having to remember the factor, it's stored with the type. -Steve
Jan 01
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 1/1/20 4:06 PM, Steven Schveighoffer wrote:
 On 1/1/20 3:20 PM, Ola Fosheim Grøstad wrote:
 On Wednesday, 1 January 2020 at 19:01:36 UTC, Steven Schveighoffer wrote:
 That is what I use. Fixed point with a factor of power of 10. In 
 other words, a fixed point number with 2 decimal places would be 
 sufficient for such currency. When doing math on such types, you just 
 need to deal with the underlying numbers, and it works fine.
You don't need fixed point, just store cents in 64 bit floating point and you get at least the same accuracy as a 53 bit integer fixed point.
It is stored that way. Stored as a long. Just nicer to deal with printing and such. And instead of having to remember the factor, it's stored with the type.
oops, totally misread the floating point part, I thought you said 64 bit integer. It's not a *terrible* idea, but as you accumulate more errors, they will add up. In money transactions, most of the time you have a fixed resolution, and everything is rounded at every transaction. So floating point is not necessary. I'd much rather do integer. I like the exactness, and with something like checkedInt, you shouldn't have overflow problems. -Steve
Jan 03
next sibling parent Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= <ola.fosheim.grostad gmail.com> writes:
On Friday, 3 January 2020 at 14:49:34 UTC, Steven Schveighoffer 
wrote:
 It's not a *terrible* idea, but as you accumulate more errors, 
 they will add up.
I actually use base10 on the server, out of convenience and easy maintenance, but my point is that you don't have to as long as you understand what is going on under the hood. You won't get more than a cent error and very rarely, even when you don't set the rounding mode if you think about how floating point works. And one cannot set the rounding mode in JavaScript, so that is what you have to deal with. But it depends on what you do. Nobody are going to complain that they get one cent less on the invoice than in the "web shop pricing estimate".
 In money transactions, most of the time you have a fixed 
 resolution, and everything is rounded at every transaction. So 
 floating point is not necessary.
Ok, so there you have a use case. If you only add then it is no problem, but if you do more complex calculations and more complex rounding then it becomes tedious and error prone real fast. When you add various layers of rebates and sales tax and what not it can get tricky to be certain that you will stay within a fixed bit width. If the owner of the system is ok with giving a little more rebate (like a cent) then fixed point does not look like a good solution.
 I'd much rather do integer. I like the exactness, and with 
 something like checkedInt, you shouldn't have overflow problems.
Well, but how do you know that you have enough bits and how do you account for an unknown number of digits at compile time if the end user can type in several rebate multipliers? If you get an exception you still have a problem...? In the general case you basically need to use big-int, and then you might as well do base 10, IMHO.
Jan 03
prev sibling parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= <ola.fosheim.grostad gmail.com> writes:
And please forget the idea that fixed point is exact, it most 
certainly is not! :-)

Let me give an example:

Assume that you have 21% VAT and prices are given including tax, 
then in order to find the price without VAT you have to calculate 
price/1.21, but in fixed point that becomes price*100/121 which 
will have a remainder. So then you also have to deal with the 
remainder, so not exact at all.

If you want the price without VAT, rounded you'll have to do:

    (price * 200 / 242 + 1) / 2

If you want to deal with the price without VAT with no rounding, 
you will have to emulate rational numbers...
Jan 03
next sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 1/3/20 12:14 PM, Ola Fosheim Grøstad wrote:
 And please forget the idea that fixed point is exact, it most certainly 
 is not! :-)
Exactness according to the terms of what is agreed upon for discrete units. Not according to some IEEE standard where the discreteness varies based on value. If you want complete exactness, rational numbers are what you need, and I do use those in other places.
 
 Let me give an example:
 
 Assume that you have 21% VAT and prices are given including tax, then in 
 order to find the price without VAT you have to calculate price/1.21, 
 but in fixed point that becomes price*100/121 which will have a 
 remainder. So then you also have to deal with the remainder, so not 
 exact at all.
 
 If you want the price without VAT, rounded you'll have to do:
 
     (price * 200 / 242 + 1) / 2
 
 If you want to deal with the price without VAT with no rounding, you 
 will have to emulate rational numbers...
 
 
You are starting from an inexact figure to begin with (almost certainly it has been rounded). Of course your figures will not be exact, in any number system. If tax is added on top of the total taxable cost, then a per-item cost is going to be inexact anyway (some will have one extra cent added due to the rounding). Generally you are only dealing with transaction amounts in full cents, unless there is some electronic form of payment, and in that case, you have a limit as to what you are dealing with. I'll take inexactness based on rounding, but consistent and agreed upon rules, over inexactness of actual value, any day. I'll give you an example, you go to the store and an item is on sale for 3 for $5.00. The cost for the first item is $1.67, the cost for the second item is $1.67, the cost for the third is $1.66. Total is $5. But if you buy one item, it's $1.67. I can understand the rules without any issue. I can't tell you what happens when you multiply 5.0/3.0 * 3.0 (or even 500.0/3.0 * 3.0). Most likely it's fine, but I can't tell you that for sure it is going to be fine. You can create systems that have rules for how to deal with imprecision, but you need to base it on types that are easy to predict and don't require IEEE experts to understand. -Steve
Jan 03
parent Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= <ola.fosheim.grostad gmail.com> writes:
On Friday, 3 January 2020 at 17:45:44 UTC, Steven Schveighoffer 
wrote:
 If tax is added on top of the total taxable cost, then a 
 per-item cost is going to be inexact anyway (some will have one 
 extra cent added due to the rounding).
Yes, although in Norway you have to give prices including VAT to consumers, but not to businesses where excluded VAT is the norm. So to get nice looking figures like "9.90" to consumers or to businesses you can either set the price including/excluding VAT and calculate the other unit doing multiplication/division. (The "law" is that the VAT is added, but that is only in book keeping).
 You can create systems that have rules for how to deal with 
 imprecision, but you need to base it on types that are easy to 
 predict and don't require IEEE experts to understand.
In that case you have to rule out fixed point and you'll have to settle for a large base 10 type. I think that is going a bit far. In my book it all depends on the situation, and the programmer is obviously part of that, but so is the platform (e.g. javascript), laws and regulations, whether it is for estimates or invoices, if it is used for accounting or sales pitch, etc etc.
Jan 03
prev sibling parent Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= <ola.fosheim.grostad gmail.com> writes:
On Friday, 3 January 2020 at 17:14:06 UTC, Ola Fosheim Grøstad 
wrote:
 If you want the price without VAT, rounded you'll have to do:

    (price * 200 / 242 + 1) / 2
Hah! Another fixed point mistake!! It should have been: (price * 200 / 121 + 1) / 2 This is what makes fixed point a chore, it is hard to read and tricky to maintain, even when it was easy to write...
Jan 04
prev sibling parent reply IGotD- <nise nise.com> writes:
On Wednesday, 1 January 2020 at 20:20:14 UTC, Ola Fosheim Grøstad 
wrote:
 On Wednesday, 1 January 2020 at 19:01:36 UTC, Steven 
 Schveighoffer wrote:
 That is what I use. Fixed point with a factor of power of 10. 
 In other words, a fixed point number with 2 decimal places 
 would be sufficient for such currency. When doing math on such 
 types, you just need to deal with the underlying numbers, and 
 it works fine.
You don't need fixed point, just store cents in 64 bit floating point and you get at least the same accuracy as a 53 bit integer fixed point.
Using floating point is not recommended. For some fractional number it is actually impossible to store the value as a rational binary number. For example 8.90 would be stored as 8.89999999999+ (in reality this is binary values so just think of my example in an equivalent binary value case). This would lead to some rounding errors, especially when chaining several operations. You should go for a representation that always calculates the currency exact, down to the cent or whatever it is. Not doing so you might even be breaking the law for some appliances.
Jan 01
parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= <ola.fosheim.grostad gmail.com> writes:
On Wednesday, 1 January 2020 at 22:04:00 UTC, IGotD- wrote:
 Using floating point is not recommended. For some fractional 
 number it is actually impossible to store the value as a 
 rational binary number. For example 8.90 would be stored as 
 8.89999999999+ (in reality this is binary values so just think 
 of my example in an equivalent binary value case). This would 
 lead to some rounding errors, especially when chaining several 
 operations.
No, not if you store as cents. You get the exact same values as with 53 bits integers with IEEE754. You have to pay atttentition to rounding mode, even-odd is common.
 You should go for a representation that always calculates the 
 currency exact, down to the cent or whatever it is. Not doing 
 so you might even be breaking the law for some appliances.
Double will do that fine. As I said, same as integer. If you need half-cents, just multiply with 200 instead of 100. Or you could use millis (multiply by 1000).
Jan 01
parent reply Basile B. <b2.temp gmx.com> writes:
On Wednesday, 1 January 2020 at 22:26:30 UTC, Ola Fosheim Grøstad 
wrote:
 On Wednesday, 1 January 2020 at 22:04:00 UTC, IGotD- wrote:
 Using floating point is not recommended. For some fractional 
 number it is actually impossible to store the value as a 
 rational binary number. For example 8.90 would be stored as 
 8.89999999999+ (in reality this is binary values so just think 
 of my example in an equivalent binary value case). This would 
 lead to some rounding errors, especially when chaining several 
 operations.
No, not if you store as cents. You get the exact same values as with 53 bits integers with IEEE754. You have to pay atttentition to rounding mode, even-odd is common.
 You should go for a representation that always calculates the 
 currency exact, down to the cent or whatever it is. Not doing 
 so you might even be breaking the law for some appliances.
Double will do that fine. As I said, same as integer. If you need half-cents, just multiply with 200 instead of 100. Or you could use millis (multiply by 1000).
Yyou're all silly guys. Don't use floating point for currency, just make a Currency fixed point custom type.
Jan 02
parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= <ola.fosheim.grostad gmail.com> writes:
On Thursday, 2 January 2020 at 13:58:17 UTC, Basile B. wrote:
 Yyou're all silly guys. Don't use floating point for currency, 
 just make a Currency fixed point custom type.
Drop the adhominem. If you understand the floating point hardware specification then there are no issues with floating point.
Jan 02
next sibling parent reply Russel Winder <russel winder.org.uk> writes:
On Thu, 2020-01-02 at 14:34 +0000, Ola Fosheim Gr=C3=B8stad via Digitalmars=
-
d wrote:
=20
[=E2=80=A6]
 If you understand the floating point hardware specification then=20
 there are no issues with floating point.
=20
I am not an expert in this, but I have seen lots of rants and arguments on this over the years. As I understand it the core problem is needing base 10 numbers not base 2 ones =E2=80=93 converting between them causes problems =E2=80=93 and usin= g hardware floating point does not have enough accuracy for compliance with requirements of FCA for financial calculations. There are various people on the ACCU general mailing list and the LJC emailing list who know much more about this than I do as they work on these things on a daily basis. I could ask there for pointers as to why money types are such an obsession.=20 --=20 Russel. =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D Dr Russel Winder t: +44 20 7585 2200 41 Buckmaster Road m: +44 7770 465 077 London SW11 1EN, UK w: www.russel.org.uk
Jan 02
parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= <ola.fosheim.grostad gmail.com> writes:
On Thursday, 2 January 2020 at 17:18:04 UTC, Russel Winder wrote:
 On Thu, 2020-01-02 at 14:34 +0000, Ola Fosheim Grøstad via 
 Digitalmars- d wrote:
 
[…]
 If you understand the floating point hardware specification 
 then there are no issues with floating point.
 
I am not an expert in this, but I have seen lots of rants and arguments on this over the years. As I understand it the core problem is needing base 10 numbers not base 2 ones – converting between them causes problems – and using hardware floating point does not have enough accuracy for compliance with requirements of FCA for financial calculations.
Oh, I am not arguing that banking institutions should use base 2 floating point. 64 bit floating point base 2 can only represent 90 trillion cents with exact precision, so if they do calculations involving base 10 constants that could easily lead to problems... (And jurisdictions have various rules for how to deal with the sub-cent remainder I believe). But you have the same issues with bit-limited base-2 fixed point, and it is easy to make mistakes with fixed bit fix point in non-trivial formulas! So, if correctness is important then you could use decimal (the exact Pythonic numeric type), large base-10 floats (rare in hardware), or big-int rationals (in this case base2 is ok, you take care of the remainder at the end)... Power9 from IBM apparently has 128 bits floating point, and IIRC some IBM machines have base 10 floating point hardware, but that is rather esoteric... In the more mundane world of every-day-computing you have to be able to represent currency in web clients and in TypeScript/JavaScript 64bit IEEE754 is the only reasonable alternative. So if you want the same representation on the server and the client then it can be useful to use 64bit floating point. Although, it is sometimes useful to store them ints as Steve suggested.
Jan 02
next sibling parent Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= <ola.fosheim.grostad gmail.com> writes:
On Thursday, 2 January 2020 at 17:57:19 UTC, Ola Fosheim Grøstad 
wrote:
 Oh, I am not arguing that banking institutions should use base 
 2 floating point. 64 bit floating point base 2 can only 
 represent 90 trillion cents with exact precision,
Typo: 90 trillion USD, so 9000 trillion cents. I.e. the range is -2^53 to 2^53, so basically a 54 bit signed integer with exact representation.
Jan 02
prev sibling parent reply bauss <jj_1337 live.dk> writes:
On Thursday, 2 January 2020 at 17:57:19 UTC, Ola Fosheim Grøstad 
wrote:
 So if you want the same representation on the server and the 
 client then it can be useful to use 64bit floating point.
Disagree with that. You shouldn't even do money calculations on the client side and thus you only need the representation of it and hence you can just send it formatted as a string. Of course if your client is not JS or anything similar then there is no problem since you can represent the value just fine.
Jan 02
parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= <ola.fosheim.grostad gmail.com> writes:
On Thursday, 2 January 2020 at 18:30:54 UTC, bauss wrote:
 You shouldn't even do money calculations on the client side and 
 thus you only need the representation of it and hence you can 
 just send it formatted as a string.

 Of course if your client is not JS or anything similar then 
 there is no problem since you can represent the value just fine.
As I've pointed out double can represent anything you can represent as 53 bits unsigned integers, so this is a non-issue... It can be very useful to do calculations on currencies on the client side, but you don't use it for any other purpose than display.
Jan 02
parent reply bachmeier <no spam.net> writes:
On Thursday, 2 January 2020 at 19:03:47 UTC, Ola Fosheim Grøstad 
wrote:
 On Thursday, 2 January 2020 at 18:30:54 UTC, bauss wrote:
 You shouldn't even do money calculations on the client side 
 and thus you only need the representation of it and hence you 
 can just send it formatted as a string.

 Of course if your client is not JS or anything similar then 
 there is no problem since you can represent the value just 
 fine.
As I've pointed out double can represent anything you can represent as 53 bits unsigned integers, so this is a non-issue... It can be very useful to do calculations on currencies on the client side, but you don't use it for any other purpose than display.
I'll just throw out that Lua has no integer type: https://www.lua.org/pil/2.3.html I've never had a reason to avoid integers for performance reasons, but I suppose there are cases in which it's relevant...
Jan 02
next sibling parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= <ola.fosheim.grostad gmail.com> writes:
On Thursday, 2 January 2020 at 19:12:11 UTC, bachmeier wrote:
 I've never had a reason to avoid integers for performance 
 reasons, but I suppose there are cases in which it's relevant...
Scripting languages try to keep things simple, so having only one numeric type is an advantage from an implementation point of view. So, not really for performance. However, if you mean fixed point integers, then you need a rather big bit width to avoid overflow errors to do the same calculations as with floating point. And it is easy to make mistakes, but older DSPs didn't have floating point units so they used fixed point math (and also older computer games) and programmers had to be very clever to make it perform well with good accuracy. In some ways fixed point is more tiresome than writing in assembly if you try to do something nontrivial. E.g. in fixed point you would have to do 3% of 123 as (123*3+50)/100... Or 3.14% of 123 as (123*314+5000)/1000. Except division is slow so you would instead convert the divisor into a reciprocal and do a multiply instead (modern compilers do this for you if the divisor is known at compile time). So if the percentage isn't known at compile time you have much work to do as a programmer to get it right...
Jan 02
parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= <ola.fosheim.grostad gmail.com> writes:
On Thursday, 2 January 2020 at 19:25:08 UTC, Ola Fosheim Grøstad 
wrote:
 (123*3+50)/100... Or 3.14% of 123 as (123*314+5000)/1000.
So easy to make mistakes with fixed point, should've been /10000...
 Except division is slow so you would instead convert the 
 divisor into a reciprocal and do a multiply instead (modern 
 compilers do this for you if the divisor is known at compile 
 time).
Well, in this specific case you could probably do result=percentage*value like this: result = (value*percentage_reciprocal+5)/10; but that is just one simple formula... it gets tricky real fast when trying to do something more advanced in fixed point efficiently and correctly.
Jan 02
parent Guillaume Piolat <first.last gmail.com> writes:
On Thursday, 2 January 2020 at 19:59:09 UTC, Ola Fosheim Grøstad 
wrote:
 On Thursday, 2 January 2020 at 19:25:08 UTC, Ola Fosheim 
 Grøstad wrote:
 (123*3+50)/100... Or 3.14% of 123 as (123*314+5000)/1000.
So easy to make mistakes with fixed point, should've been /10000...
The devil goes even further if you want to account for negative numbers :) Integer rounding is actually kind of hellish vs floating-point rounding.
Jan 02
prev sibling parent reply lithium iodate <whatdoiknow doesntexist.net> writes:
On Thursday, 2 January 2020 at 19:12:11 UTC, bachmeier wrote:
 I'll just throw out that Lua has no integer type:
 https://www.lua.org/pil/2.3.html

 I've never had a reason to avoid integers for performance 
 reasons, but I suppose there are cases in which it's relevant...
Lua 5.3 (released five years ago) has got integers. The text you are linking is 17 years old.
Jan 02
parent reply bachmeier <no spam.net> writes:
On Thursday, 2 January 2020 at 20:05:18 UTC, lithium iodate wrote:
 On Thursday, 2 January 2020 at 19:12:11 UTC, bachmeier wrote:
 I'll just throw out that Lua has no integer type:
 https://www.lua.org/pil/2.3.html

 I've never had a reason to avoid integers for performance 
 reasons, but I suppose there are cases in which it's 
 relevant...
Lua 5.3 (released five years ago) has got integers. The text you are linking is 17 years old.
Okay, I'm not a Lua expert, obviously. The point still stands that you can have a successful programming language without even having an integer type.
Jan 02
parent reply JN <666total wp.pl> writes:
On Thursday, 2 January 2020 at 21:03:42 UTC, bachmeier wrote:
 Okay, I'm not a Lua expert, obviously. The point still stands 
 that you can have a successful programming language without 
 even having an integer type.
*cough* *cough* JavaScript *cough*
Jan 02
parent Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= <ola.fosheim.grostad gmail.com> writes:
On Thursday, 2 January 2020 at 21:31:02 UTC, JN wrote:
 On Thursday, 2 January 2020 at 21:03:42 UTC, bachmeier wrote:
 Okay, I'm not a Lua expert, obviously. The point still stands 
 that you can have a successful programming language without 
 even having an integer type.
*cough* *cough* JavaScript *cough*
JavaScript has 32 bit integers as an implict type though.
Jan 02
prev sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Thu, Jan 02, 2020 at 05:18:04PM +0000, Russel Winder via Digitalmars-d wrote:
[...]
 As I understand it the core problem is needing base 10 numbers not
 base 2 ones – converting between them causes problems – and using
 hardware floating point does not have enough accuracy for compliance
 with requirements of FCA for financial calculations.
AFAIK, the crux of the problem with using binary floating point types for money representation is that the former's base-2 rounding behaviour does not 100% match the latter's expected base-10 rounding behaviour. This discrepancy is a big problem because in financial applications 100% identical behaviour with base-10 rounding is a non-negotiable requirement. T -- Having a smoking section in a restaurant is like having a peeing section in a swimming pool. -- Edward Burr
Jan 02
parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= <ola.fosheim.grostad gmail.com> writes:
On Thursday, 2 January 2020 at 17:59:40 UTC, H. S. Teoh wrote:
 AFAIK, the crux of the problem with using binary floating point 
 types for money representation is that the former's base-2 
 rounding behaviour does not 100% match the latter's expected 
 base-10 rounding behaviour. This discrepancy is a big problem 
 because in financial applications 100% identical behaviour with 
 base-10 rounding is a non-negotiable requirement.
You can set the rounding mode. Round to even is the common default I believe (bankers rounding). Although I think it is common to just round up at 0.5 when doing manual accounting. So you can get the same behaviour when storing in cents, but you then need to do the downscaling as the last step. So you need to take care to get correct results. E.g. to get 3% of 123 cents: (3*123.0)/100 Although 25% is no problem: 0.25*123.0 (since 0.25 has an exact representation in base 2 floating point) So it is easy to make mistakes. Same argument for fixed point. With base 10 representation that is no longer a problem. Same with base 2 rational, as you do the rounding in the last step.
Jan 02
next sibling parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= <ola.fosheim.grostad gmail.com> writes:
On Thursday, 2 January 2020 at 18:20:38 UTC, Ola Fosheim Grøstad 
wrote:
 E.g. to get 3% of 123 cents: (3*123.0)/100
Whoops, too quick, that won't work. You need to set the rounding mode first and then do the truncating. E.g. you can do the same calculation twice with round up/round down and do basic rounding. If you get the same result, then it is right. But easy to make mistakes... like I just demonstrated.. :-P
Jan 02
next sibling parent Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= <ola.fosheim.grostad gmail.com> writes:
On Thursday, 2 January 2020 at 18:26:25 UTC, Ola Fosheim Grøstad 
wrote:
 truncating.  E.g. you can do the same calculation twice with 
 round up/round down and do basic rounding. If you get the same 
 result, then it is right. But easy to make mistakes... like I 
 just demonstrated.. :-P
For those interested there work done on a standard for doing exactly this (interval arithmetics) called IEEE1788: https://en.wikipedia.org/wiki/Interval_arithmetic#IEEE_Std_1788-2015_%E2%80%93_IEEE_standard_for_interval_arithmetic So all computations are done twice, once rounding up and once rounding down. If the error gets too large then you can switch to a slower algorithm. So then you can safely do base 2 floating points using fast hardware without all the issues and fall back on a slower base 10 software implementation where it fails.
Jan 02
prev sibling parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= <ola.fosheim.grostad gmail.com> writes:
On Thursday, 2 January 2020 at 18:26:25 UTC, Ola Fosheim Grøstad 
wrote:
 On Thursday, 2 January 2020 at 18:20:38 UTC, Ola Fosheim 
 Grøstad wrote:
 E.g. to get 3% of 123 cents: (3*123.0)/100
Whoops, too quick, that won't work.
For the curious, on some FPUs you can test if the computations was inexact using the FE_INEXACT flag. So, then you can do all the computations and ensure that there is no information loss up to the division. Then you do an inexact division with rounding-down mode: // set rounding down mode here and track FE_INEXACT exception x = 3.0*value+0.5 // ensure that FE_INEXACT is false // do inexact computation result = floor(x/100.0) I think that should work out ok for all input values, but I've never actually used FE_INEXACT in practice, so maybe I got something wrong...
Jan 02
parent Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= <ola.fosheim.grostad gmail.com> writes:
On Thursday, 2 January 2020 at 23:13:00 UTC, Ola Fosheim Grøstad 
wrote:
 // set rounding down mode here and track FE_INEXACT exception
 x = 3.0*value+0.5
x = 3.0*value+50.0 … ;-) In addtion to the IEEE754 base10 D-implementation that exists as a dub package I found this implementation by Intel: https://software.intel.com/en-us/articles/intel-decimal-floating-point-math-library Clock cycle counts from 2018: https://www.lirmm.fr/arith18/papers/CorneaM_Decimal_ARITH18.pdf It apparently supports Linux, Windows and Mac so it might be worth creating bindings for it if nobody has done it already? This combined with IEEE1788 could be interesting, first do the computation with base2 hardware floats and revert to base 10 on failure. Base 2 is basically 100 times faster...
Jan 03
prev sibling parent bauss <jj_1337 live.dk> writes:
On Thursday, 2 January 2020 at 18:20:38 UTC, Ola Fosheim Grøstad 
wrote:
 You can set the rounding mode. Round to even is the common 
 default I believe (bankers rounding). Although I think it is 
 common to just round up at 0.5 when doing manual accounting.
You are right about this and the reason is that otherwise large numbers start being very inaccurate very fast.
Jan 02
prev sibling parent bachmeier <no spam.net> writes:
On Wednesday, 1 January 2020 at 18:16:01 UTC, Vitaly Livshic 
wrote:
  Thanks Steve, JN.

  JN, BigInt is big integer. It cannot works with fractional 
 numbers. It is sadly, that wide-spread languages have no 
 convinient money type. This is abnormal.
  Hope, D will have fixed point type either as built-in type or 
 library.
https://stackoverflow.com/questions/3730019/why-not-use-double-or-float-to-represent-currency
 It cannot works with fractional numbers.
Nothing can give you a better answer if your fraction is 176/100. You can always convert from int to some other type without losing precision on the initial calculations if you're in a situation where you want to calculate the per-unit cost for 37 units that cost a total of $500.17.
Jan 01
prev sibling next sibling parent bauss <jj_1337 live.dk> writes:
On Wednesday, 1 January 2020 at 15:41:32 UTC, Vitaly Livshic 
wrote:
 Good day.

 I came from Java world, where 'double' type inadequate for 
 money calculation. BigDecimal serves for it. This is ugly type, 
 but gives precise results.

 Which type I must use for money in D?
You can use this package: https://code.dlang.org/packages/money
Jan 01
prev sibling parent Rumbu <rumbu rumbu.ro> writes:
On Wednesday, 1 January 2020 at 15:41:32 UTC, Vitaly Livshic 
wrote:
 Good day.

 I came from Java world, where 'double' type inadequate for 
 money calculation. BigDecimal serves for it. This is ugly type, 
 but gives precise results.

 Which type I must use for money in D?
Here you have a full IEE754-2008 compliant implementation of decimal data types (32, 64 and 128 bit). https://github.com/rumbu13/decimal Same as the Intel one proposed here by someone else, but written completely in D, no dependencies, no bindings.
Jan 04