Hi,
Squeak compiler compiles numbers like "3.4" into SmallFloat types. But Squeak can also handle number pairs like Point and Fractions. Has anyone considered decimals into fractions, instead? Like how we treat fractions in real life? $0.02 is really 2/100th of a dollar. I wouldn't want it to float ;-). Squeak could compile fix points like "3.4" into fractions like "34/10" and compile into float only if an explicit exponent is given like in "3.4e0". This would help us maintain accuracy as long as possible in fraction arithmetic we use in daily life. e.g. 3.41 - 3.40 0.010000000000000231 vs ((341/100) - (340/100)) asFloat 0.01 Regards .. Subbu |
Hi, Yes sure, that's a possibility. But that means changing well established Smalltalk syntax, and thus modify a huge library of source code... We currently have ScaledDecimal with 3.41s or 3.42s2 syntax. The problem with ScaledDecimal is that their printing is ambiguous: two different ScaledDecimal may print the same. {1.0s2 / 3.0s2. 0.33s2} collect: #printString. {1.0s2 / 3.0s2. 0.33s2} collect: #reciprocal. They just truncate in Squeak (and maybe round in Pharo, not even with banker rounding). This is because they are just Fraction in disguise, and that's their second problem: repeated computations could lead to monstruous fractions. That's why I'm interested in experiments: is their usage sustainable, or does it lead to explosion? It's very easy to experiment: introduce a new NumberParser subclass and connect it to Smalltalk Parser (via Scanner>>#xDigit). Then, carefully recompile some part of the system, and discover the vital source code where Float literals are required. We could also introduce FixedPoint, a number that would round to a fixed number of fractional digits. And maybe DecimalFixedPoint and BinaryFixedPoint for the two principal usefull flavours. The problem with FixedPoint is that we must decide in advance how many digits? A FixedPoint vs FloatingPoint is trading a bit more precision vs lot less range, so it's maybe not that intersting for a general purpose library... Or we could have a DecimalFloat, a Floating point in base 10. https://en.wikipedia.org/wiki/Decimal_floating_point Maybe that's the practical universal Number that we are after? Of course, like the other kinds above, it would be emulated... Le dim. 7 avr. 2019 à 05:10, K K Subbu <[hidden email]> a écrit : Hi, |
In reply to this post by K K Subbu
> 3.41 - 3.40 0.010000000000000231 > vs > ((341/100) - (340/100)) asFloat 0.01 On my system: [3.41 - 3.40] bench --> '76,200,000 per second. 13.1 nanoseconds per run.' [((341/100) - (340/100)) asFloat] bench --> '1,230,000 per second. 816 nanoseconds per run.' a := 341/100. b := 340/100. [a - b] bench --> '4,050,000 per second. 247 nanoseconds per run.' Stef |
In reply to this post by Nicolas Cellier
On 07/04/19 7:46 PM, Nicolas Cellier wrote:
> Hi, > Yes sure, that's a possibility. > But that means changing well established Smalltalk syntax, and thus > modify a huge library of source code... I am not suggesting any changes to syntax (yet!). I was exploring an idea of storing decimal numbers like '3.41' in a lossless form and convert to float on demand (possibly cached). Internal format would be totally transparent to the rest of the code. We already do these for integers: (1 / 1) class SmallInteger (1 / 3) class Fraction (3 / 1) class SmallInteger > We currently have ScaledDecimal with 3.41s or 3.42s2 syntax. > The problem with ScaledDecimal is that their printing is ambiguous: two > different ScaledDecimal may print the same. > > {1.0s2 / 3.0s2. 0.33s2} collect: #printString. > {1.0s2 / 3.0s2. 0.33s2} collect: #reciprocal. Is Squeak 5.3alpha/64b/Linux, I get {1.0s2 / 3.0s2. 0.33s2} collect: #reciprocal {3.00s2 . 3.03s2}. But I get your point. For decimal numbers, using both denominator and scale is an overkill. Printing can be handled by the existing printFractionAs... . Reciprocal of a decimal number could become imprecise operation, so we could fall back to Float when necessary. With decimal numbers, we could print 0.25 reciprocal = 4 instead of the current 0.25 reciprocal = 4.166666666666667 > They just truncate in Squeak (and maybe round in Pharo, not even with > banker rounding). > This is because they are just Fraction in disguise, and that's their > second problem: repeated computations could lead to monstruous fractions. Like Pi you mean ;-). True. But, most real life scenarios involve only small denominators like 2, 4, 5, 100 or 1000. Large denominators could be handled through exceptions and reduction through gcd. > That's why I'm interested in experiments: is their usage sustainable, or > does it lead to explosion? > It's very easy to experiment: introduce a new NumberParser subclass and > connect it to Smalltalk Parser (via Scanner>>#xDigit). > Then, carefully recompile some part of the system, and discover the > vital source code where Float literals are required. Thanks a ton for your encouraging words and the tips. I will give them a try. > Or we could have a DecimalFloat, a Floating point in base 10. > https://en.wikipedia.org/wiki/Decimal_floating_point > Maybe that's the practical universal Number that we are after? > Of course, like the other kinds above, it would be emulated... A variant - DecimalNumber. It would extend the idea of place values to the negative axis without the drawbacks of IEEE754-2008. I suppose it would be computationally inefficient compared to Float, but definitely more accurate in common scenarios. I would love to see "0.25 reciprocal" evaluate to 4 ;-). Regards .. Subbu |
In reply to this post by Stéphane Rollandin
On 07/04/19 8:07 PM, Stéphane Rollandin wrote:
> >> 3.41 - 3.40 0.010000000000000231 >> vs >> ((341/100) - (340/100)) asFloat 0.01 > > On my system: > > [3.41 - 3.40] bench > --> '76,200,000 per second. 13.1 nanoseconds per run.' > > [((341/100) - (340/100)) asFloat] bench > --> '1,230,000 per second. 816 nanoseconds per run.' > > a := 341/100. > b := 340/100. > [a - b] bench > --> '4,050,000 per second. 247 nanoseconds per run.' Thank you for the quick response. The slowdown is to be expected. Floats are immediate while Fractions are objects. I floated a strawman proposal to tradeoff accuracy for speed. Regards .. Subbu |
In reply to this post by K K Subbu
On 08/04/19 2:52 PM, K K Subbu wrote:
> >> We currently have ScaledDecimal with 3.41s or 3.42s2 syntax. >> The problem with ScaledDecimal is that their printing is ambiguous: >> two different ScaledDecimal may print the same. >> >> {1.0s2 / 3.0s2. 0.33s2} collect: #printString. >> {1.0s2 / 3.0s2. 0.33s2} collect: #reciprocal. > > Is Squeak 5.3alpha/64b/Linux, I get > > {1.0s2 / 3.0s2. 0.33s2} collect: #reciprocal {3.00s2 . 3.03s2}. > > But I get your point. For decimal numbers, using both denominator and > scale is an overkill. Printing can be handled by the existing > printFractionAs... . Reciprocal of a decimal number could become > imprecise operation, so we could fall back to Float when necessary. > > With decimal numbers, we could print > 0.25 reciprocal = 4 > instead of the current > 0.25 reciprocal = 4.166666666666667 PBKAC! mea culpa. I meant to say I expected 4 instead of 4.0. ScaledDecimal looks promising but I don't know why it doesn't reduce like Fraction: self assert: (0.25s reciprocal class) classAndValueEquals: 4 Regards .. Subbu |
In reply to this post by K K Subbu
> Thank you for the quick response. The slowdown is to be expected. Floats
> are immediate while Fractions are objects. I floated a strawman proposal > to tradeoff accuracy for speed. In my own software, and for example in my games, speed is often paramount. I got some spectacular improvements on overall performance by liberaly using #asFloat. I even implemented #/// as follow: Number /// aNumber ^ self / aNumber asFloat for cases (which I found many) where I want to ensure that a division will never return a Fraction. Stef |
Free forum by Nabble | Edit this page |