ScaledDecimal fromString: gives a Float

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
6 messages Options
Reply | Threaded
Open this post in threaded view
|

ScaledDecimal fromString: gives a Float

Peter Goodall-4
Had some financial calculations fail on me the other day because I didn't
realize the following is  true:

(ScaledDecimal fromString: '3.123') class = Float

I sort of think that if I ask for a ScaledDecimal I should get one, or an
error.

Opinions?


Regards,

--Peter Goodall


Reply | Threaded
Open this post in threaded view
|

Re: ScaledDecimal fromString: gives a Float

Chris Uppal-3
Peter Goodall wrote:

> (ScaledDecimal fromString: '3.123') class = Float
>
> I sort of think that if I ask for a ScaledDecimal I should get one, or an
> error.

I agree.

I think the underlying problem is that the Dolphin facilities for converting
Strings to numbers are implemented in terms of
Number class>>readSmalltalkSyntaxFrom:, and that is just wrong for any
application that happens not to be a Smalltalk compiler.

I don't think that either the API that's provided, or the implementation
thereof, are suitable for general purpose applications.

This whole area is overdue for an overhaul, IMO.

(Although I should say that I, at least, haven't often been affected by the
problems -- and where I have been I've just switched to doing the number
parsing myself)

    -- chris


Reply | Threaded
Open this post in threaded view
|

Re: ScaledDecimal fromString: gives a Float

Ian Bartholomew-18
In reply to this post by Peter Goodall-4
Peter,

> I sort of think that if I ask for a ScaledDecimal I should get one,
> or an error.
>
> Opinions?

Personally I think it should be an error.  You've asked for an instance
of a given class and then passed it a String which doesn't contain a
valid sequence of characters for an instance of that class.  It's a bit
like the discussion the other day about what "Float fromString: '.5'"
should answer.

Dolphin does seem a bit, umm, undecided about what to do in these
situations though....

Integer fromString: '3.123'
Throws an error (because . is not a valid character)

Fraction fromString: '3.123'
Answers an Fraction which is an approximation of the argument (fair
enough)

Fraction fromString: '3'
Fraction fromString: '3.0'
Both answer a SmallInteger (rather than the result of "Fraction
numerator: 3(.0) denominator: 1(.0)")

Float fromString: '3'
Answers a Float (and not a SmallInteger as Fraction does)

I'm not sure what the best solution is, especially as changing any of
the existing behaviour may break existing code.  One possibility might
be making the #fromString: conversion in each class strict (if it isn't
the correct format, or a permissible variant, then an error is raised)
and a generic Number>>fromString: method that, as now, answers an
instance of the appropriate subclass.  You could then provide reasonable
(?) #asXxxx methods in all Number subclasses that always answers (via
double dispatch?) an instance of the required class.  You could then use

ANumberSubclass fromString: 'whatever'
if you were sure of (or wanted to ensure) the correct format and

(Number fromString: 'whatever') asANumberSubclass
if you weren't

The first would always give as good a conversion as possible but the
second might be less exact e.g. "(Number fromString: '3.123')
asScaledDecimal" will probably not equal "ScaledDecimal fromString:
'3.123s'" because of the formers Float->Fraction->ScaledDecimal
conversions.

--
Ian

Use the Reply-To address to contact me.
Mail sent to the From address is ignored.


Reply | Threaded
Open this post in threaded view
|

Re: ScaledDecimal fromString: gives a Float

Bob Jarvis
In reply to this post by Peter Goodall-4
"Peter Goodall" <[hidden email]> wrote in message news:<3ff4cdab$0$18691$[hidden email]>...
> Had some financial calculations fail on me the other day because I didn't
> realize the following is  true:
>
> (ScaledDecimal fromString: '3.123') class = Float
>
> I sort of think that if I ask for a ScaledDecimal I should get one, or an
> error.
>
> Opinions?

Look at the comment in Number>>fromString:.  It says

    "Should be separate fromDisplayString: version which converts from
locale specific format (this method from Smalltalk format)"

So it looks like you should use #fromDisplayString: here instead of
#fromString: to get the results you want.

Sadly, #fromDisplayString: is not implemented.  :-(

Happily, #fromDisplayString: shouldn't be too tough to implement.  A
starting point might be something like

    Number>>fromDisplayString: aString
        | num num2 |
        num := self fromString: aString.
        num2 := num.
        [ num2 := num perform: ('as', self name) asSymbol ]
            on: Error do: [ :e | ].
        ^num2

This won't work for ScaledDecimals because there is no
#asScaledDecimal method in Number (there is an asScaledDecimal:
method), but this could be worked around (i.e. implement
ScaledDecimal>>fromDisplayString:).  Just a thought.


Reply | Threaded
Open this post in threaded view
|

Re: ScaledDecimal fromString: gives a Float

Peter Goodall-4
In reply to this post by Ian Bartholomew-18
Thanks Ian,

"Ian Bartholomew" <[hidden email]> wrote in message
news:bt3u36$31n51$[hidden email]...

> Peter,
>
> > I sort of think that if I ask for a ScaledDecimal I should get one,
> > or an error.
> >
> > Opinions?
>
> Personally I think it should be an error.  You've asked for an instance
> of a given class and then passed it a String which doesn't contain a
> valid sequence of characters for an instance of that class.  It's a bit
> like the discussion the other day about what "Float fromString: '.5'"
> should answer.

Which is why I considered an error as a result. Though much like the
'.5' and Float problem - the input  String can be perfectly well represented
as a ScaledDecimal. Noting that the 'scale' part of the ScaledDecimal
does not affect the arithmetic operarations of the number, just its display.
So a default scale for ScaledDecimal may be reasonable.

I reckon a reasonable return from a reasonable request is a good thing.

>
> Dolphin does seem a bit, umm, undecided about what to do in these
> situations though....
>
> Integer fromString: '3.123'
> Throws an error (because . is not a valid character)
 Its a valid number representation though. In a workspace
3.123 asInteger = 3

>
> Fraction fromString: '3.123'
> Answers an Fraction which is an approximation of the argument (fair
> enough)

A fraction can represent an terminating decimal perfectly. In fact, the
Scaled Decimal is represented internally as a Fraction.
(I always wanted a Fractional Arithmetic Coprocessor - but could never find
one)

>
> Fraction fromString: '3'
3 asFraction --> 3

 Fraction fromString: '3.0'
3.0 asFraction --> 3
( 3/1) class = SmallInteger

As I was going through this - I was thinking that the solution
was for #fromString to do the same as the Compiler - may not be a
complete answer though.

> Both answer a SmallInteger (rather than the result of "Fraction
> numerator: 3(.0) denominator: 1(.0)")
>
> Float fromString: '3'
> Answers a Float (and not a SmallInteger as Fraction does)
>
> I'm not sure what the best solution is, especially as changing any of
> the existing behaviour may break existing code.  One possibility might
> be making the #fromString: conversion in each class strict (if it isn't
> the correct format, or a permissible variant, then an error is raised)
> and a generic Number>>fromString: method that, as now, answers an
> instance of the appropriate subclass.  You could then provide reasonable
> (?) #asXxxx methods in all Number subclasses that always answers (via
> double dispatch?) an instance of the required class.  You could then use
>
> ANumberSubclass fromString: 'whatever'
> if you were sure of (or wanted to ensure) the correct format and
>
> (Number fromString: 'whatever') asANumberSubclass
> if you weren't
>
> The first would always give as good a conversion as possible but the
> second might be less exact e.g. "(Number fromString: '3.123')
> asScaledDecimal" will probably not equal "ScaledDecimal fromString:
> '3.123s'" because of the formers Float->Fraction->ScaledDecimal
> conversions.
Sounds good.
>
> --
> Ian
>
> Use the Reply-To address to contact me.
> Mail sent to the From address is ignored.
>


Reply | Threaded
Open this post in threaded view
|

Re: ScaledDecimal fromString: gives a Float

Ian Bartholomew-18
Peter,

> Which is why I considered an error as a result. Though much like the
> '.5' and Float problem - the input  String can be perfectly well
> represented as a ScaledDecimal. Noting that the 'scale' part of the
> ScaledDecimal
> does not affect the arithmetic operarations of the number, just its
> display. So a default scale for ScaledDecimal may be reasonable.

But if you are not going to worry about the scale factor (until you need
to display the current value anyway) then you might as well use a Float
or Fraction and avoid the extra complications of a SD.

> I reckon a reasonable return from a reasonable request is a good
> thing.

Who is going to define "reasonable" though :-)

is "(Fraction fromString: '3') class = SmallInteger" a reasonable
return?.  How about "Integer fromString: 'three'" being a reasonable
request :-)

I'm not just trying to be argumentative here, honestly, but I do think a
slightly more structured approach for the various #fromString: methods
in the _base_ image would have been better.  Probably too late now
though....

>> Integer fromString: '3.123'
>> Throws an error (because . is not a valid character)
>  Its a valid number representation though. In a workspace
> 3.123 asInteger = 3

Yes, but there are two conversions involved it that expression.  The
compiler does the initial (and completely valid) "Float fromString:
'3.123'" and you then send asInteger to the resulting Float.  It's
equivalent to

(Float fromString: '3.123') asInteger

which would still be possible in the scheme I outlined yesterday.

> A fraction can represent an terminating decimal perfectly. In fact,
> the Scaled Decimal is represented internally as a Fraction.

Yes, but if you do anything other than a String->Fraction conversion
then you will run into trouble. For example

3.123 asFraction = (216085600644019/69191674878008)
rather than the (rather more obvious) 3123/1000.

Should you expect

(3.123 asScaledDecimal: 3) = (ScaledDecimal fromString: '3.123s3')

to answer true?

> As I was going through this - I was thinking that the solution
> was for #fromString to do the same as the Compiler - may not be a
> complete answer though.

I don't think there is a complete answer.  I have to say though that, as
Chris mentioned yesterday, I haven't really had any major (i.e.
unsolvable) problems in this area - if the existing conversions aren't
what I want then I just add a method that is....

--
Ian

Use the Reply-To address to contact me.
Mail sent to the From address is ignored.