Hi all!
Why does evaluating the following: FLOAT new value: 1.2 result in: a FLOAT(1.20000004768372) Is this a bug or a result of my misuse of FLOAT? If its a misuse, how would one setup a float in external memory? btw - running D4 on NT4. thx Eric |
Eric Winger wrote:
> > Hi all! > > Why does evaluating the following: > > FLOAT new value: 1.2 > > result in: > > a FLOAT(1.20000004768372) > > Is this a bug or a result of my misuse of FLOAT? > [snip] Eric, I don't really understand what you're trying to do here. Is the problem that you are interfacing with external code and you are surprised to find a flonum you set to 1.2 on the outside become something else within Dolphin? Or are you surprised that when you set a Dolphin number to a putative flonum you can manipulate it and expose such far-to-the-right rubbish? How did you get that value anyway? I mean, there's no "value:" message appropriate in that context. "value:" is used to feed parameters to blocks, a.k.a., supply lambda expressions with arguments. If r denotes your number, why not just write r := 1.2. and be done with it? Anyway, I did: r := 1.2. f := StdioFileStream write: 'foo.txt'. r printOn: f decimalPlaces: 30. f close. and got 1.200000000000000000000000000000 in the file "foo.txt". On the other hand, it may be that you're struggling with a "loss of innocence" discovering there are "more things in heav'n and earth...than are dreamt on in the" integers. In that case you are in need of spiritual guidance. Check out: http://camden-www.rutgers.edu/HELP/Documentation/Sun-compiler-docs/WS6/manuals/common/ncg/ncg_goldberg.html Hope you don't mind the gentle teasing.... (;-)} Feel free to write back. -- --------------------------------------------------------------------- Jan Theodore Galkowski [hidden email] The Smalltalk Idiom [hidden email] ********************************************************************* "Smalltalk? Yes, it's really that slick." --------------------------------------------------------------------- Want to know more? Check out http://www.dnsmith.com/SmallFAQ/ http://www.object-arts.com/DolphinWhitePaper.htm http://st-www.cs.uiuc.edu/users/johnson/smalltalk/ ********************************************************************* |
Jan Theodore Galkowski wrote:
> > Eric Winger wrote: > >>Hi all! >> >>Why does evaluating the following: >> >>FLOAT new value: 1.2 >> >>result in: >> >>a FLOAT(1.20000004768372) >> >>Is this a bug or a result of my misuse of FLOAT? >> >> > > [snip] > > Eric, > > I don't really understand what you're trying to do here. Is the > problem that you are interfacing with external code and you are > surprised to find a flonum you set to 1.2 on the outside become > something else within Dolphin? No, I'm testing some COM code and when I create an external structure float object (i.e. instance of class FLOAT), I discovered a bug in my code in that what was being created and sent out through an interface (1.2) was coming back as 1.20000004768372). Thus my test would break. So, after breaking down the code a smidgen, I narrowed it down to something reproducable that could be posted. The way I created my external float seemed odd. But I did it that way because I couldn't find any instance creation methods in a FLOAT object and no conversion method in Float. So I really wasn't sure if I should have even expected the statement FLOAT new value: 1.2. (how the float was created in Dolphin) to be a proper way to create a float that was suitable for passing in COM. Something that the implementation object would receive as 1.2. Or, if I had stumbled onto a bug. > Or are you surprised that when > you set a Dolphin number to a putative flonum you can manipulate > it and expose such far-to-the-right rubbish? How did you > get that value anyway? I mean, there's no "value:" message > appropriate in that context. "value:" is used to feed parameters > to blocks, a.k.a., supply lambda expressions with arguments. Value: is the protocol that external structures get their underlying reference or "value" values set in Dolphin. IOW, its how the underlying address or bytes for an ST object in external memory. See any subclass of ExternalStructure for a more specific example. > > If r denotes your number, why not just write > > r := 1.2. > > and be done with it? see above > > Anyway, I did: > > r := 1.2. > f := StdioFileStream write: 'foo.txt'. > r printOn: f decimalPlaces: 30. > f close. > > and got > > 1.200000000000000000000000000000 > > in the file "foo.txt". > > On the other hand, it may be that you're struggling with > a "loss of innocence" discovering there are "more things in > heav'n and earth...than are dreamt on in the" integers. In > that case you are in need of spiritual guidance. > Check out: > > http://camden-www.rutgers.edu/HELP/Documentation/Sun-compiler-docs/WS6/manuals/common/ncg/ncg_goldberg.html > > Hope you don't mind the gentle teasing.... (;-)} > Feel free to write back. > > Don't mind...When you start with a question like, "my code doesn't work", you try to narrow it down to the specific problem for posting. Sometimes you narrow it to far and the context is lost. Eric |
In reply to this post by Eric Winger-4
Eric,
> Is this a bug or a result of my misuse of FLOAT? Neither, it's the inherent imprecision of floating point numbers that cannot be exactly represented in a binary form. It happens in all languages and cannot be avoided without changing to another form of representation. If you were to try it again with a number that can be expressed exactly in binary, 1.5 for example, then everything should work as expected. Dolphin Floats are held in 64 bit values so any inaccuracy is correspondingly small (around the 15th DP IIRC). When displaying Floats Dolphin takes account of this and hides (rounds out) or ignores the extra digits (see the Float class variables DefaultSigFigs and SignificantDifference) FLOATs on the other hand are stored as 32 bit values so the inaccuracy is larger (each bit means more). Dolphin still displays it as a Float, expecting the imprecision to be the same as for a 64 bit value, and therefore doesn't hide the extra digits (the 8th DP in your example) The fact that the imprecision is still present in Dolphin Floats can be seen by accumulation over a number of iterations f := 1.2. t := 0.0. 10000000 timesRepeat: [t := t + f]. t inspect -> answers 11999999.9986769 as well as things like 0.2 + 0.2 + 0.2 = 0.6 -> answers false Regards Ian |
In reply to this post by Eric Winger-4
Eric
You wrote in message news:[hidden email]... > Hi all! > > Why does evaluating the following: > > FLOAT new value: 1.2 > > result in: > > a FLOAT(1.20000004768372) > > > Is this a bug or a result of my misuse of FLOAT? It is neither, it is a precision error. FLOAT is an ExternalStructure for buffering 32-bit FP numbers. The internal Float class if 64-bit. Historical note: Some Smalltalk's have 32 and 64-bit FP representations in the Number hierarchy, reasoning that 32-bit FP isn't really used much these days we decided, as they have in Squeak, to support only 64-bit double-precision. When you access the value of a FLOAT the VM primitive responsible promotes the single-precision number to double-precision, and the result is inaccurate. > If its a misuse, how would one setup a float in external memory? First check that you don't need a DOUBLE. Assuming that the API you are using really is spec'd in terms of single-precision FLOATs, then you are doing it right. Of course it is only necessary to use FLOAT/DOUBLE when the FP numbers in question are [output] parameters, or are embedded in structs/arrays. For normal call out purposes you can use the float/double argument type and let the VM marshall the Float object appropriately. Regards Blair |
In reply to this post by Eric Winger-5
Eric,
> No, I'm testing some COM code and when I create an external structure > float object (i.e. instance of class FLOAT), I discovered a bug in my > code in that what was being created and sent out through an interface > (1.2) was coming back as 1.20000004768372). Thus my test would break. Hmmm, I was going to suggest that you round the values first - ((FLOAT new value: 1.2) asFloat roundTo: 0.001) = (1.2 roundTo: 0.001) which is one of the ways of comparing floats. To use the example I posted before ((0.2 + 0.2 + 0.2) roundTo: 0.001) = 0.6 now answers true. However I did a bit more testing using .. 1 to: 100 do: [:top | top to: 100 do: [:bottom | v := top asFloat / bottom asFloat. ((FLOAT new value: v) asFloat roundTo: 0.001) = (v roundTo: 0.001) ifFalse: [Transcript print: v; cr]]] and it still fails with a small number of values. I'm not sure why, although I think it's something to do will increasing the precision loss with the extra conversions for Float>>FLOAT>>Float. Using #truncateTo: instead of #roundTo: makes it even worse? I'll have another think later but, hopefully, someone else will come up with a better solution. Regards Ian |
In reply to this post by Blair McGlashan
Hi Blair,
> It is neither, it is a precision error. FLOAT is an ExternalStructure for > buffering 32-bit FP numbers. The internal Float class if 64-bit. Historical > note: Some Smalltalk's have 32 and 64-bit FP representations in the Number > hierarchy, reasoning that 32-bit FP isn't really used much these days we > decided, as they have in Squeak, to support only 64-bit double-precision. > When you access the value of a FLOAT the VM primitive responsible promotes > the single-precision number to double-precision, and the result is > inaccurate. Two reasons that come to mind for using single vs. double precision are: (1) minimizing storage space when dealing with huge arrays; (2) experimenting with roundoff error. Especially in the case of BIG data sets, one would probably write something in C/C++ (FORTRAN is too scary to think about<g>) and package it in a DLL. The only trouble spot that I can think of would be having the VM do a JIT conversion of an intentionally single precision number to double; one might want to see the single precision results in their unaltered state. Of course, the "visualization" could be done in C too, or by bypassing Float and directly parsing the bytes of the FLOAT (details left as an excise for the reader<g>). Also, I've frequently had my doubts as to whether it's possible to control precision in C; FORTRAN at least appears to offer far better control over it, because that's what it was designed to do. With respect to controlling precision to watch results, I'm undecided on whether that's a holdover from debates about how to represent floats (how to split the bits between precision and range), or a really great idea that's getting obscured by FPUs, cheap memory, and really cool adaptive step size algorithms. Any takers? Ironically, the only recent problem I've had with conversion between float/double was in that auto-generated database front end =:0 I ended up selecting single precision floats in the database itself, and the VM did the same thing you described. Once I realized that it was conversion related (wasn't sure exactly where it was happening though), I changed the numeric fields to double in the database. The existing records still contained "noise" (we've since fixed them), and new numbers are stored/retrieved as entered. Have a good one, Bill -- Wilhelm K. Schwab, Ph.D. [hidden email] |
In reply to this post by Ian Bartholomew-5
Ian, Eric
"Ian Bartholomew" <[hidden email]> wrote in message news:2a6I7.4852$h4.336192@stones... > Eric, > > > No, I'm testing some COM code and when I create an external structure > > float object (i.e. instance of class FLOAT), I discovered a bug in my > > code in that what was being created and sent out through an interface > > (1.2) was coming back as 1.20000004768372). Thus my test would break. > > Hmmm, I was going to suggest that you round the values first - > > ((FLOAT new value: 1.2) asFloat roundTo: 0.001) = (1.2 roundTo: 0.001) > ... We have this handy method in our TestCase hierarchy: compare: x to: y epsilon: epsilon "Compare that the two <Float> values are equivalent to within the specified <Float> difference." | max | ^(max := x abs max: y abs) <= epsilon or: [(x - y) abs < (epsilon * max)] It is in fact a parameterized version of the Float>>equals: method. A suitable epsilon for single-precision floats would be about 1.0e-6 (though in fact your test seems to run fine down to 1e-7, failing at 1e-8). Regards Blair ------------------------------------------------------------ TestCase subclass: #FLOATTest instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' classInstanceVariableNames: ''! FLOATTest comment: 'SUnitBrowser openOnTestCase: self'! !FLOATTest methodsFor! compare: x to: y epsilon: epsilon "Compare that the two <Float> values are equivalent to within the specified <Float> difference." | max | ^(max := x abs max: y abs) <= epsilon or: [(x - y) abs < (epsilon * max)]! testFLOATConversions 1 to: 100 do: [:top | top to: 100 do: [:bottom | | v | v := top asFloat / bottom asFloat. self assert: (self compare: (FLOAT new value: v) asFloat to: v epsilon: 1.0e-006)]]! ! |
In reply to this post by Blair McGlashan
Blair McGlashan wrote:
> ..... > >>If its a misuse, how would one setup a float in external memory? >> > > First check that you don't need a DOUBLE. Assuming that the API you are > using really is spec'd in terms of single-precision FLOATs, then you are > doing it right. Of course it is only necessary to use FLOAT/DOUBLE when the > FP numbers in question are [output] parameters, or are embedded in > structs/arrays. For normal call out purposes you can use the float/double > argument type and let the VM marshall the Float object appropriately. > > The spec in question is an [out] spec & is indeed a float. To get by my TestCase test bug, I just rounded the comparison values to 0.1 precision. I'll keep an eye on it when when I move to actually using the code between processes. I added an instance creation method fromFloat: so that it would be more symmetric with ExternalInteger creations. DOUBLE should probably have one too. Not sure why this was left out. Thanks all, Eric |
In reply to this post by Blair McGlashan
Blair McGlashan wrote:
> > Ian, Eric > > "Ian Bartholomew" <[hidden email]> wrote in message > news:2a6I7.4852$h4.336192@stones... > > Eric, > > Blair, YES! This was the direction I was heading with my general query about Eric's situation. Indeed, the upshot of "what every...should know about flonums" is that traditional equality is not defined for floating point numbers and an epsilon-based test is needed. Indeed, one will find this presentation in almost any book on numerical analysis and it is a cornerstone of numerical work since LINPACK first came out. The definitive guide to numerical computing with Smalltalk is the great and recent OBJECT-ORIENTED IMPLEMENTATION OF NUMERICAL METHODS by Didier Besset which belongs, I believe, in every Smalltalkers library. He's got a section 1.5 dealing with comparison of flonums which discusses this. Besset is available via bookpool.com at http://www.bookpool.com/.x/oh64r7hlri/ss/1?qs=Besset&Go.x=7&Go.y=7 [snip] > > We have this handy method in our TestCase hierarchy: > compare: x to: y epsilon: epsilon > "Compare that the two <Float> values are equivalent to within the > specified <Float> difference." > > | max | > ^(max := x abs max: y abs) <= epsilon > or: [(x - y) abs < (epsilon * max)] > > It is in fact a parameterized version of the Float>>equals: method. A > suitable epsilon for single-precision floats would be about 1.0e-6 (though > in fact your test seems to run fine down to 1e-7, failing at 1e-8). [snip] -- --------------------------------------------------------------------- Jan Theodore Galkowski [hidden email] The Smalltalk Idiom [hidden email] ********************************************************************* "Smalltalk? Yes, it's really that slick." --------------------------------------------------------------------- Want to know more? Check out http://www.dnsmith.com/SmallFAQ/ http://www.object-arts.com/DolphinWhitePaper.htm http://st-www.cs.uiuc.edu/users/johnson/smalltalk/ ********************************************************************* |
Free forum by Nabble | Edit this page |