Silly question about storing and displaying currency amounts?
Hi everyone. I have a little bank analysis package for my own use but having trouble displaying and storing amounts. I parse out a CSV file from the bank and convert the amounts from text to ScaledDecimal. I then store these into a STON file, which converts them back to text. I then read them in and convert them back to ScaledDecimal again. I am not used to ~Pharo have spent 24 years using VisualAge Smalltalk so I need a little bit of help because I am getting 1 Penny errors in the conversions. I can cope with this but I would like to get it right. Can anyone give me a simple means of managing, say, an amount like £76.49 from the bank so that it stops coming back to me as £76.48? David Totally Objects
David,
On 30 Aug 2021, at 14:02, David Pennington <david@totallyobjects.com> wrote:
Hi everyone. I have a little bank analysis package for my own use but having trouble displaying and storing amounts. I parse out a CSV file from the bank and convert the amounts from text to ScaledDecimal. I then store these into a STON file, which converts them back to text. I then read them in and convert them back to ScaledDecimal again.
I am not used to ~Pharo have spent 24 years using VisualAge Smalltalk so I need a little bit of help because I am getting 1 Penny errors in the conversions. I can cope with this but I would like to get it right.
Can anyone give me a simple means of managing, say, an amount like £76.49 from the bank so that it stops coming back to me as £76.48?
David Totally Objects
Working with money is always challenging. I know that many people say 'Use ScaledDecimal' as if that magically solves all problems but it does not. ScaledDecimal is a bit of a dangerous class to use. It is just a fraction with a certain precision. Internally it might be pretty accurate with respect to most operations, its external representation as floating point number can be misleading, as this representation is prone to all problems related to floating point (including the fact that decimal and binary floating point are not the same). STON does not doing anything wrong as far as I know. Consider the following: | amount ston | amount := 76.49 asScaledDecimal. ston := STON toString: amount. STON fromString: ston. (STON fromString: ston) = amount. "true" | amount ston | amount := 76.49 asScaledDecimal: 2. ston := STON toString: amount. STON fromString: ston. (STON fromString: ston) = amount. "true" | amount ston | amount := 76.49. ston := STON toString: amount. STON fromString: ston. (STON fromString: ston) = amount. "true" BUT, 76.49 asScaledDecimal already has your penny loss (if rounded to pennies). HTH, Sven
hi, some time ago there was a "money" package around, that basically had a Currency type that internally stored values as integers (like, 42.0 was becoming 4200). This had some advantages to make this kind of calculus (also, it has the "other" advantage of making you believe that you have a lot more money than what you have ;). maybe you need to go that direction? Esteban On Aug 30 2021, at 2:31 pm, Sven Van Caekenberghe <sven@stfx.eu> wrote:
David,
On 30 Aug 2021, at 14:02, David Pennington <david@totallyobjects.com> wrote:
Hi everyone. I have a little bank analysis package for my own use but having trouble displaying and storing amounts. I parse out a CSV file from the bank and convert the amounts from text to ScaledDecimal. I then store these into a STON file, which converts them back to text. I then read them in and convert them back to ScaledDecimal again.
I am not used to ~Pharo have spent 24 years using VisualAge Smalltalk so I need a little bit of help because I am getting 1 Penny errors in the conversions. I can cope with this but I would like to get it right.
Can anyone give me a simple means of managing, say, an amount like £76.49 from the bank so that it stops coming back to me as £76.48?
David Totally Objects
Working with money is always challenging. I know that many people say 'Use ScaledDecimal' as if that magically solves all problems but it does not. ScaledDecimal is a bit of a dangerous class to use. It is just a fraction with a certain precision. Internally it might be pretty accurate with respect to most operations, its external representation as floating point number can be misleading, as this representation is prone to all problems related to floating point (including the fact that decimal and binary floating point are not the same). STON does not doing anything wrong as far as I know. Consider the following: | amount ston | amount := 76.49 asScaledDecimal. ston := STON toString: amount. STON fromString: ston. (STON fromString: ston) = amount. "true"
| amount ston | amount := 76.49 asScaledDecimal: 2. ston := STON toString: amount. STON fromString: ston. (STON fromString: ston) = amount. "true"
| amount ston | amount := 76.49. ston := STON toString: amount. STON fromString: ston. (STON fromString: ston) = amount. "true"
BUT, 76.49 asScaledDecimal already has your penny loss (if rounded to pennies). HTH, Sven
I was recently bitten by this wrong "scaled decimal expectation", in which what I thought was wrong it actually wasn't, or I can't tell whether it was. I would expect that a scaled decimal would be something like what Esteban L. suggests, an integer where you just move the decimal point for printing, and also a number where you lose precision due to truncation. So this: ((1.0s2 / 3) * 3) + 0.01s2 Would yield 1 instead of "1.01s2" So if you use the "integer only" representation, it is very much like any currency number would me managed: ((100 / 3) truncated * 3 + 1) / 100 This, of course, yields the right value, but it started truncated from the beginning. (0.33s2 * 3) + 0.01s2 "1" I would appreciate any pointers about learning more about this. It's embarrassing for me not knowing how each type should work in order to tell what's right from what's wrong. Regards! ps: In my case this wasn't because of money, but another kind of use, but the situation is the same. Esteban A. Maringolo On Mon, Aug 30, 2021 at 9:51 AM Esteban Lorenzano <estebanlm@netc.eu> wrote:
hi,
some time ago there was a "money" package around, that basically had a Currency type that internally stored values as integers (like, 42.0 was becoming 4200). This had some advantages to make this kind of calculus (also, it has the "other" advantage of making you believe that you have a lot more money than what you have ;).
maybe you need to go that direction?
Esteban
On Aug 30 2021, at 2:31 pm, Sven Van Caekenberghe <sven@stfx.eu> wrote:
David,
On 30 Aug 2021, at 14:02, David Pennington <david@totallyobjects.com> wrote:
Hi everyone. I have a little bank analysis package for my own use but having trouble displaying and storing amounts. I parse out a CSV file from the bank and convert the amounts from text to ScaledDecimal. I then store these into a STON file, which converts them back to text. I then read them in and convert them back to ScaledDecimal again.
I am not used to ~Pharo have spent 24 years using VisualAge Smalltalk so I need a little bit of help because I am getting 1 Penny errors in the conversions. I can cope with this but I would like to get it right.
Can anyone give me a simple means of managing, say, an amount like £76.49 from the bank so that it stops coming back to me as £76.48?
David Totally Objects
Working with money is always challenging. I know that many people say 'Use ScaledDecimal' as if that magically solves all problems but it does not. ScaledDecimal is a bit of a dangerous class to use. It is just a fraction with a certain precision. Internally it might be pretty accurate with respect to most operations, its external representation as floating point number can be misleading, as this representation is prone to all problems related to floating point (including the fact that decimal and binary floating point are not the same).
STON does not doing anything wrong as far as I know. Consider the following:
| amount ston | amount := 76.49 asScaledDecimal. ston := STON toString: amount. STON fromString: ston. (STON fromString: ston) = amount. "true"
| amount ston | amount := 76.49 asScaledDecimal: 2. ston := STON toString: amount. STON fromString: ston. (STON fromString: ston) = amount. "true"
| amount ston | amount := 76.49. ston := STON toString: amount. STON fromString: ston. (STON fromString: ston) = amount. "true"
BUT, 76.49 asScaledDecimal already has your penny loss (if rounded to pennies).
HTH,
Sven
Hi Sven, I just tried 46.79 asScaledDecimal and 46.79 asScaledDecimal: 2 in both VAST and Pharo and see quite different results. In VAST, not providing a scale returns 46.79, but with a scale of 14 (46.79s14). But it is still 46.79. What's good is that the results of "asScaledDecimal: 2" yields the same results on VAST and Pharo. Your examples also use a fixed scale of 2. So is it safe to assume that when you provide the scale, ScaledDecimal is good for monetary amounts in Pharo? I ask because that is what I once learned in an IBM training decades ago: use ScaledDecimals for money and percentages, never use floats. Would you agree that as a rule of thumb, Pounds, Dollars and Euros are safely stored and processed when you make sure to always provide the scale of 2 (unless you manage stock or petrol prices which use 3 or 4 or even more digits)? Joachim Am 30.08.21 um 14:31 schrieb Sven Van Caekenberghe:
David,
On 30 Aug 2021, at 14:02, David Pennington <david@totallyobjects.com> wrote:
Hi everyone. I have a little bank analysis package for my own use but having trouble displaying and storing amounts. I parse out a CSV file from the bank and convert the amounts from text to ScaledDecimal. I then store these into a STON file, which converts them back to text. I then read them in and convert them back to ScaledDecimal again.
I am not used to ~Pharo have spent 24 years using VisualAge Smalltalk so I need a little bit of help because I am getting 1 Penny errors in the conversions. I can cope with this but I would like to get it right.
Can anyone give me a simple means of managing, say, an amount like £76.49 from the bank so that it stops coming back to me as £76.48?
David Totally Objects Working with money is always challenging. I know that many people say 'Use ScaledDecimal' as if that magically solves all problems but it does not. ScaledDecimal is a bit of a dangerous class to use. It is just a fraction with a certain precision. Internally it might be pretty accurate with respect to most operations, its external representation as floating point number can be misleading, as this representation is prone to all problems related to floating point (including the fact that decimal and binary floating point are not the same).
STON does not doing anything wrong as far as I know. Consider the following:
| amount ston | amount := 76.49 asScaledDecimal. ston := STON toString: amount. STON fromString: ston. (STON fromString: ston) = amount. "true"
| amount ston | amount := 76.49 asScaledDecimal: 2. ston := STON toString: amount. STON fromString: ston. (STON fromString: ston) = amount. "true"
| amount ston | amount := 76.49. ston := STON toString: amount. STON fromString: ston. (STON fromString: ston) = amount. "true"
BUT, 76.49 asScaledDecimal already has your penny loss (if rounded to pennies).
HTH,
Sven
-- ----------------------------------------------------------------------- Objektfabrik Joachim Tuchel mailto:jtuchel@objektfabrik.de Fliederweg 1 http://www.objektfabrik.de D-71640 Ludwigsburg http://joachimtuchel.wordpress.com Telefon: +49 7141 56 10 86 0 Fax: +49 7141 56 10 86 1
http://smalltalkhub.com/pdebruic/Money/index.html Am Mo., 30. Aug. 2021 um 14:51 Uhr schrieb jtuchel@objektfabrik.de < jtuchel@objektfabrik.de>:
Hi Sven,
I just tried 46.79 asScaledDecimal and 46.79 asScaledDecimal: 2 in both VAST and Pharo and see quite different results. In VAST, not providing a scale returns 46.79, but with a scale of 14 (46.79s14). But it is still 46.79.
What's good is that the results of "asScaledDecimal: 2" yields the same results on VAST and Pharo.
Your examples also use a fixed scale of 2. So is it safe to assume that when you provide the scale, ScaledDecimal is good for monetary amounts in Pharo? I ask because that is what I once learned in an IBM training decades ago: use ScaledDecimals for money and percentages, never use floats.
Would you agree that as a rule of thumb, Pounds, Dollars and Euros are safely stored and processed when you make sure to always provide the scale of 2 (unless you manage stock or petrol prices which use 3 or 4 or even more digits)?
Joachim
Am 30.08.21 um 14:31 schrieb Sven Van Caekenberghe:
David,
On 30 Aug 2021, at 14:02, David Pennington <david@totallyobjects.com> wrote:
Hi everyone. I have a little bank analysis package for my own use but having trouble displaying and storing amounts. I parse out a CSV file from the bank and convert the amounts from text to ScaledDecimal. I then store these into a STON file, which converts them back to text. I then read them in and convert them back to ScaledDecimal again.
I am not used to ~Pharo have spent 24 years using VisualAge Smalltalk so I need a little bit of help because I am getting 1 Penny errors in the conversions. I can cope with this but I would like to get it right.
Can anyone give me a simple means of managing, say, an amount like £76.49 from the bank so that it stops coming back to me as £76.48?
David Totally Objects Working with money is always challenging. I know that many people say 'Use ScaledDecimal' as if that magically solves all problems but it does not. ScaledDecimal is a bit of a dangerous class to use. It is just a fraction with a certain precision. Internally it might be pretty accurate with respect to most operations, its external representation as floating point number can be misleading, as this representation is prone to all problems related to floating point (including the fact that decimal and binary floating point are not the same).
STON does not doing anything wrong as far as I know. Consider the following:
| amount ston | amount := 76.49 asScaledDecimal. ston := STON toString: amount. STON fromString: ston. (STON fromString: ston) = amount. "true"
| amount ston | amount := 76.49 asScaledDecimal: 2. ston := STON toString: amount. STON fromString: ston. (STON fromString: ston) = amount. "true"
| amount ston | amount := 76.49. ston := STON toString: amount. STON fromString: ston. (STON fromString: ston) = amount. "true"
BUT, 76.49 asScaledDecimal already has your penny loss (if rounded to pennies).
HTH,
Sven
-- ----------------------------------------------------------------------- Objektfabrik Joachim Tuchel mailto:jtuchel@objektfabrik.de Fliederweg 1 http://www.objektfabrik.de D-71640 Ludwigsburg http://joachimtuchel.wordpress.com Telefon: +49 7141 56 10 86 0 Fax: +49 7141 56 10 86 1
I come from a currency trader background and way back in 1983, I was dealing with this in North Star Basic and then UCSD Pascal and into VisualSmalltalk (remember that?) so I have some history here. Trouble is, at 76, my little grey cells are letting me down bit. Excuses over. I will go back into the code and make sure that I scale all these ScaledDecimals to 2 and see if that helps. BTW, I wasnât suggesting that there was any problem with STON as that works brilliantly for me. David
On 30 Aug 2021, at 13:51, jtuchel@objektfabrik.de wrote:
Hi Sven,
I just tried 46.79 asScaledDecimal and 46.79 asScaledDecimal: 2 in both VAST and Pharo and see quite different results. In VAST, not providing a scale returns 46.79, but with a scale of 14 (46.79s14). But it is still 46.79.
What's good is that the results of "asScaledDecimal: 2" yields the same results on VAST and Pharo.
Your examples also use a fixed scale of 2. So is it safe to assume that when you provide the scale, ScaledDecimal is good for monetary amounts in Pharo? I ask because that is what I once learned in an IBM training decades ago: use ScaledDecimals for money and percentages, never use floats.
Would you agree that as a rule of thumb, Pounds, Dollars and Euros are safely stored and processed when you make sure to always provide the scale of 2 (unless you manage stock or petrol prices which use 3 or 4 or even more digits)?
Joachim
Am 30.08.21 um 14:31 schrieb Sven Van Caekenberghe:
David,
On 30 Aug 2021, at 14:02, David Pennington <david@totallyobjects.com> wrote:
Hi everyone. I have a little bank analysis package for my own use but having trouble displaying and storing amounts. I parse out a CSV file from the bank and convert the amounts from text to ScaledDecimal. I then store these into a STON file, which converts them back to text. I then read them in and convert them back to ScaledDecimal again.
I am not used to ~Pharo have spent 24 years using VisualAge Smalltalk so I need a little bit of help because I am getting 1 Penny errors in the conversions. I can cope with this but I would like to get it right.
Can anyone give me a simple means of managing, say, an amount like £76.49 from the bank so that it stops coming back to me as £76.48?
David Totally Objects Working with money is always challenging. I know that many people say 'Use ScaledDecimal' as if that magically solves all problems but it does not. ScaledDecimal is a bit of a dangerous class to use. It is just a fraction with a certain precision. Internally it might be pretty accurate with respect to most operations, its external representation as floating point number can be misleading, as this representation is prone to all problems related to floating point (including the fact that decimal and binary floating point are not the same).
STON does not doing anything wrong as far as I know. Consider the following:
| amount ston | amount := 76.49 asScaledDecimal. ston := STON toString: amount. STON fromString: ston. (STON fromString: ston) = amount. "true"
| amount ston | amount := 76.49 asScaledDecimal: 2. ston := STON toString: amount. STON fromString: ston. (STON fromString: ston) = amount. "true"
| amount ston | amount := 76.49. ston := STON toString: amount. STON fromString: ston. (STON fromString: ston) = amount. "true"
BUT, 76.49 asScaledDecimal already has your penny loss (if rounded to pennies).
HTH,
Sven
-- ----------------------------------------------------------------------- Objektfabrik Joachim Tuchel mailto:jtuchel@objektfabrik.de Fliederweg 1 http://www.objektfabrik.de D-71640 Ludwigsburg http://joachimtuchel.wordpress.com Telefon: +49 7141 56 10 86 0 Fax: +49 7141 56 10 86 1
Ah, you have run into the insufficiently well known "ScaledDecimal looks like what you need but isn't". In VisualAge, you have a data type that is basically a reflection of iBM mainframe "packed decimal" format, and perfectly suits the needs of an SQL or COBOL interface. It gives you round-tripping with no error. In my Smalltalk, ScaledDecimal is a pair (n,s) where n and s are unrestricted integers, representing n * (10 raisedTo: s). This also gives you exact round-tripping and suits the needs of an SQL or COBOL interface. However, in Squeak and Pharo, ScaledDecimal is something else entirely. Basically, a pair (q,s) where q is an Integer or Fraction, and s is a scale factor used for *printing*. This doesn't just lead to oddities like two unequal ScaledDecimals printing identically, it leads to rounding issues. This is not a bug, it is ScaledDecimal working as designed. Working within Squeak, the simplest approach is to represent money as pence, and the next simplest is to make or find a Money class. On Tue, 31 Aug 2021 at 00:02, David Pennington <david@totallyobjects.com> wrote:
Hi everyone. I have a little bank analysis package for my own use but having trouble displaying and storing amounts. I parse out a CSV file from the bank and convert the amounts from text to ScaledDecimal. I then store these into a STON file, which converts them back to text. I then read them in and convert them back to ScaledDecimal again.
I am not used to ~Pharo have spent 24 years using VisualAge Smalltalk so I need a little bit of help because I am getting 1 Penny errors in the conversions. I can cope with this but I would like to get it right.
Can anyone give me a simple means of managing, say, an amount like £76.49 from the bank so that it stops coming back to me as £76.48?
David Totally Objects
Hi all, In the past months I worked a bit with it and found many problems too, like: - doing scaled decimal arithmetics losing precision https://github.com/pharo-project/pharo/issues/8668 - strange default scale values https://github.com/pharo-project/pharo/issues/8669 I also got hit with âdowngrading" issues during my vacation coding. For example, when operating a scaled decimal with a float, the result gets downgraded to a float. See 17.777 asScaledDecimal + 0.0 >From all the comments I understand that the current implementation is not very useful, they all lead to âdo not use it, instead do XXXâ where XXX is either - write your own - load that other package - use an integer to represent pennies This this means that either it can be removed, or it can benefit with a new and better implementation. I think it would be interesting to have a better ScaledDecimal implementation, that not only does precise printing but also precise arithmetics. Or maybe somebody has a good implementation already available? Cheers, Guille > El 31 ago 2021, a las 0:58, Richard O'Keefe <raoknz@gmail.com> escribió: > > Ah, you have run into the insufficiently well known > "ScaledDecimal looks like what you need but isn't". > > In VisualAge, you have a data type that is basically a reflection of iBM > mainframe "packed decimal" format, and perfectly suits the needs of > an SQL or COBOL interface. It gives you round-tripping with no error. > > In my Smalltalk, ScaledDecimal is a pair (n,s) where n and s are > unrestricted integers, representing n * (10 raisedTo: s). This also > gives you exact round-tripping and suits the needs of an SQL or COBOL > interface. > > However, in Squeak and Pharo, ScaledDecimal is something else > entirely. Basically, a pair (q,s) where q is an Integer or Fraction, > and s is a scale factor used for *printing*. This doesn't just lead to > oddities like two unequal ScaledDecimals printing identically, it > leads to rounding issues. This is not a bug, it is ScaledDecimal working > as designed. > > Working within Squeak, the simplest approach is to represent money as > pence, and the next simplest is to make or find a Money class. > > On Tue, 31 Aug 2021 at 00:02, David Pennington <david@totallyobjects.com> wrote: >> >> Hi everyone. I have a little bank analysis package for my own use but having trouble displaying and storing amounts. I parse out a CSV file from the bank and convert the amounts from text to ScaledDecimal. I then store these into a STON file, which converts them back to text. I then read them in and convert them back to ScaledDecimal again. >> >> I am not used to ~Pharo have spent 24 years using VisualAge Smalltalk so I need a little bit of help because I am getting 1 Penny errors in the conversions. I can cope with this but I would like to get it right. >> >> Can anyone give me a simple means of managing, say, an amount like £76.49 from the bank so that it stops coming back to me as £76.48? >> >> David >> Totally Objects
I have ScaledDecimal, based on the ANSI Smalltalk standard, but fixed point decimal arithmetic, because that's what the ANSI Smalltalk standard calls for (citing a non-existent part of the LIA standard) but contradicts FixedPoint, a version of that specifically intended for plugging into Squeak and/or Pharo, the snag being that I don't seem to have kept the change set, just the master. I can recreate and donate the change set if anyone would like it. PackedDecimal, which just models VisualAge 31-digit packed decimal for interoperability, but does not do arithmetic. On Tue, 31 Aug 2021 at 19:44, Guillermo Polito <guillermopolito@gmail.com> wrote:
Hi all,
In the past months I worked a bit with it and found many problems too, like: - doing scaled decimal arithmetics losing precision https://github.com/pharo-project/pharo/issues/8668 - strange default scale values https://github.com/pharo-project/pharo/issues/8669
I also got hit with âdowngrading" issues during my vacation coding. For example, when operating a scaled decimal with a float, the result gets downgraded to a float. See
17.777 asScaledDecimal + 0.0
From all the comments I understand that the current implementation is not very useful, they all lead to âdo not use it, instead do XXXâ where XXX is either - write your own - load that other package - use an integer to represent pennies
This this means that either it can be removed, or it can benefit with a new and better implementation. I think it would be interesting to have a better ScaledDecimal implementation, that not only does precise printing but also precise arithmetics.
Or maybe somebody has a good implementation already available?
Cheers, Guille
El 31 ago 2021, a las 0:58, Richard O'Keefe <raoknz@gmail.com> escribió:
Ah, you have run into the insufficiently well known "ScaledDecimal looks like what you need but isn't".
In VisualAge, you have a data type that is basically a reflection of iBM mainframe "packed decimal" format, and perfectly suits the needs of an SQL or COBOL interface. It gives you round-tripping with no error.
In my Smalltalk, ScaledDecimal is a pair (n,s) where n and s are unrestricted integers, representing n * (10 raisedTo: s). This also gives you exact round-tripping and suits the needs of an SQL or COBOL interface.
However, in Squeak and Pharo, ScaledDecimal is something else entirely. Basically, a pair (q,s) where q is an Integer or Fraction, and s is a scale factor used for *printing*. This doesn't just lead to oddities like two unequal ScaledDecimals printing identically, it leads to rounding issues. This is not a bug, it is ScaledDecimal working as designed.
Working within Squeak, the simplest approach is to represent money as pence, and the next simplest is to make or find a Money class.
On Tue, 31 Aug 2021 at 00:02, David Pennington <david@totallyobjects.com> wrote:
Hi everyone. I have a little bank analysis package for my own use but having trouble displaying and storing amounts. I parse out a CSV file from the bank and convert the amounts from text to ScaledDecimal. I then store these into a STON file, which converts them back to text. I then read them in and convert them back to ScaledDecimal again.
I am not used to ~Pharo have spent 24 years using VisualAge Smalltalk so I need a little bit of help because I am getting 1 Penny errors in the conversions. I can cope with this but I would like to get it right.
Can anyone give me a simple means of managing, say, an amount like £76.49 from the bank so that it stops coming back to me as £76.48?
David Totally Objects
participants (8)
-
David Pennington -
Esteban Lorenzano -
Esteban Maringolo -
Guillermo Polito -
jtuchel@objektfabrik.de -
Richard O'Keefe -
Sabine Manaa -
Sven Van Caekenberghe