How to print 0.09 as 0.09 and not as 9.0e-002

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

How to print 0.09 as 0.09 and not as 9.0e-002

Theo Pronk
Anyone,

Working with amounts needs to be simple and to be displayed with 2
decimals only.

I have problems displaying some numbers example with 0.09 which shows as
9.0e-002 on a workspace.

If you try it in the PersonalAccount example, say as a new initial
balance it does the same (ie type 0.09 as an amount and it shows as
9.0e-002).

I've tried using cents only but when it comes to showing 9 cents to the
user the same problem arises (eg when divide by 100).

Do I need to start putting cents into strings and padding with zeros and
a decimal point before displaying or printing? This would imply creating
a new class for amounts which I would have expected to already be in the
standard image.

Is there any easy way?

Thanks for any help.

Theo Pronk


Reply | Threaded
Open this post in threaded view
|

Re: How to print 0.09 as 0.09 and not as 9.0e-002

Ian Bartholomew-15
Theo,

> Is there any easy way?

Depends on what you mean by easy :-)

You could store your values using the ScaledDecimal class which remembers
the number of significant decimal places. You will have to strip the
terminating 's' off when you display it as a currency value though.

s := ScaledDecimal newFromNumber: 0.09 scale: 2.
s printString ==> '0.09s'

Alternatively you could also add a method that always displays numeric
values with two decimal places. Probably best added as a method in Number

Number>>currencyPrintString
    | stream |
    stream := String writeStream.
    self asFloat printOn: stream decimalPlaces: 2.
    ^stream contents

0.09 currencyPrintString ==> '0.09'

Steve Zara created and made available an add-on Currency class but I can't
locate the URL now. Anyone know it?

Lastly, I've got a goodie available for Dolphin 4 and 5 that provides a
CurrencyToText converter. You can do things like

CurrencyToText new
    currencySymbol: $£;
    commas: true;
    integerLength: 10;
    fractionLength: 2;
    zeroFillInteger: true;
    convertFromLeftToRight: 12345.67890
==> '£0,000,012,345.68'

and for your example

CurrencyToText new
    currencySymbol: $£;
    fractionLength: 2;
    convertFromLeftToRight: 0.09
==> '£0.09'

You can download it from my (new) web site

http://www.idb.me.uk

If you have problems with that address try the old one at

http://www.iandb.org.uk

(aside) I would appreciate it if you (or anyone else who would care to try
it - pretty please) can let me know if you can or can't get through to the
first address. I'm migrating my website across to that address but it's not
proving as easy as I thought.
Please tick one
    (a) You got redirected to the old home page (recognisable by only having
one image at the top of the home page)
    (b) You got a default "Cobalt" server page
    (c) You got through.

Regards
    Ian


Reply | Threaded
Open this post in threaded view
|

Re: How to print 0.09 as 0.09 and not as 9.0e-002

Ian Bartholomew-15
> Steve Zara created and made available an add-on Currency class but I can't
> locate the URL now. Anyone know it?

Found it ...

http://www.serf.org/steve/Dolphin/


Reply | Threaded
Open this post in threaded view
|

Re: How to print 0.09 as 0.09 and not as 9.0e-002

Andy Bower
In reply to this post by Theo Pronk
Theo,

> Working with amounts needs to be simple and to be displayed with 2
> decimals only.
>
> I have problems displaying some numbers example with 0.09 which shows as
> 9.0e-002 on a workspace.
>
> If you try it in the PersonalAccount example, say as a new initial
> balance it does the same (ie type 0.09 as an amount and it shows as
> 9.0e-002).
>
> I've tried using cents only but when it comes to showing 9 cents to the
> user the same problem arises (eg when divide by 100).
>
> Do I need to start putting cents into strings and padding with zeros and
> a decimal point before displaying or printing? This would imply creating
> a new class for amounts which I would have expected to already be in the
> standard image.
>
> Is there any easy way?

First of all read the comment in Float>>printOn:. The simplest change
(although not really correct) you could make would be to just change this to
use #printOn:decimalPlaces: passing 2 as the number of places required. This
is obviously a bit of a sledgehammer approach since it changes the display
for all floats in the system regardless of whether they are currency or not.
If I were you, I'd revert out this change and continue as below.

A better approach is to realize that all display of numbers inside text
fields in the MVP framework is run through sub-instances of TypeConverter.
In particular an instance of NumberToText will be being used and the
conversion from a number to text is made with the (confusingly named)
#leftToRight: method. You'll see that NumberToText>>leftToRight: just uses
#displayString hence it picks up the default formatting.

Create a subclass of NumberToText called, say, CurrencyToText and override
the #leftToRight: method as:

leftToRight: aNumber
    | stream |
    stream := String writeStream.
    aNumber printOn: stream decimalPlaces: 2.
    ^stream contents.

Now use the ViewComposer edit any views that you have that need to use a
currency display (try PersonalMoney first). Select each of text fields in
turn and double click the #typeconverter aspect. This should pop up a choice
dialog where you can choose your newly created CurrencyToText converter. You
should then find the display of numbers more acceptable from a currency
point of view.

Best Regards,

Andy Bower
Dolphin Support
http://www.object-arts.com
---
Are you trying too hard?
http://www.object-arts.com/Relax.htm
---


Reply | Threaded
Open this post in threaded view
|

Re: How to print 0.09 as 0.09 and not as 9.0e-002

Theo Pronk
Hi Andy and Ian,

I've actually built a new class and some loose method to deal with Money
(my class definition lent from SQL server). There is still quite a lot
of tweaking to do, eg allow varying presentation $999,999.99 or allowing
a comma instead of a decimal point for decimal (like $999.999,99 as used
in Holland). Also dealing with user input errors needs work, I don't
like the idea of a wallback in a live application.

Thanks to: Steve Zara for confirming my approach, perhaps Money could be
a standard class in the future?

The code so far see attached Money.pac

Theo
======================================


Andy Bower wrote:

> Theo,
>
>
>>Working with amounts needs to be simple and to be displayed with 2
>>decimals only.
>>
>>I have problems displaying some numbers example with 0.09 which shows as
>>9.0e-002 on a workspace.
>>
>>If you try it in the PersonalAccount example, say as a new initial
>>balance it does the same (ie type 0.09 as an amount and it shows as
>>9.0e-002).
>>
>>I've tried using cents only but when it comes to showing 9 cents to the
>>user the same problem arises (eg when divide by 100).
>>
>>Do I need to start putting cents into strings and padding with zeros and
>>a decimal point before displaying or printing? This would imply creating
>>a new class for amounts which I would have expected to already be in the
>>standard image.
>>
>>Is there any easy way?
>>
>
> First of all read the comment in Float>>printOn:. The simplest change
> (although not really correct) you could make would be to just change this to
> use #printOn:decimalPlaces: passing 2 as the number of places required. This
> is obviously a bit of a sledgehammer approach since it changes the display
> for all floats in the system regardless of whether they are currency or not.
> If I were you, I'd revert out this change and continue as below.
>
> A better approach is to realize that all display of numbers inside text
> fields in the MVP framework is run through sub-instances of TypeConverter.
> In particular an instance of NumberToText will be being used and the
> conversion from a number to text is made with the (confusingly named)
> #leftToRight: method. You'll see that NumberToText>>leftToRight: just uses
> #displayString hence it picks up the default formatting.
>
> Create a subclass of NumberToText called, say, CurrencyToText and override
> the #leftToRight: method as:
>
> leftToRight: aNumber
>     | stream |
>     stream := String writeStream.
>     aNumber printOn: stream decimalPlaces: 2.
>     ^stream contents.
>
> Now use the ViewComposer edit any views that you have that need to use a
> currency display (try PersonalMoney first). Select each of text fields in
> turn and double click the #typeconverter aspect. This should pop up a choice
> dialog where you can choose your newly created CurrencyToText converter. You
> should then find the display of numbers more acceptable from a currency
> point of view.
>
> Best Regards,
>
> Andy Bower
> Dolphin Support
> http://www.object-arts.com
> ---
> Are you trying too hard?
> http://www.object-arts.com/Relax.htm
> ---
>
>
>

| package |
package := Package name: 'Money'.
package paxVersion: 0;
        basicComment: 'General purpose money class'.


package classNames
        add: #Money;
        add: #MoneyPresenter;
        add: #MoneyToText;
        yourself.

package methodNames
        add: #Number -> #asMoney;
        add: #SequenceableCollection -> #splitBeforeAndAfter:;
        add: #String -> #numericSeparateWithCommas;
        yourself.

package binaryGlobalNames: (Set new
        yourself).

package globalAliases: (Set new
        yourself).

package allResourceNames: (Set new
        add: #MoneyPresenter -> 'Default view';
        yourself).

package setPrerequisites: (IdentitySet new
        add: '..\..\Object Arts\Dolphin\Base\Dolphin';
        add: '..\..\Object Arts\Dolphin\MVP\Base\Dolphin MVP Base';
        add: '..\..\Object Arts\Dolphin\MVP\Type Converters\Dolphin Type Converters';
        yourself).

package!

"Class Definitions"!

ScaledDecimal subclass: #Money
        instanceVariableNames: 'currencySymbol'
        classVariableNames: ''
        poolDictionaries: ''
        classInstanceVariableNames: ''!
ValuePresenter subclass: #MoneyPresenter
        instanceVariableNames: ''
        classVariableNames: ''
        poolDictionaries: ''
        classInstanceVariableNames: ''!
NumberToText subclass: #MoneyToText
        instanceVariableNames: ''
        classVariableNames: ''
        poolDictionaries: ''
        classInstanceVariableNames: ''!

"Global Aliases"!


"Loose Methods"!

!Number methodsFor!

asMoney
        "Return as an Amount of money instance."
        ^Money newFromNumber: self scale: 2
! !
!Number categoriesFor: #asMoney!public! !

!SequenceableCollection methodsFor!

splitBeforeAndAfter: element
        "Return a two element array of the collections before and after the element."

        | index |
        index := self indexOf: element.
        ^Array
                with: (self copyFrom: 1 to: index - 1)
                with: (self copyFrom: index + 1)! !
!SequenceableCollection categoriesFor: #splitBeforeAndAfter:!public! !

!String methodsFor!

numericSeparateWithCommas
        "Return a string with groups of 3 digits separated by commas from the right hand side."

        | string result group |
        string := self reverse.
        result := String new.
        [string size > 3] whileTrue: [
                group := string copyFrom: 1 to: 3.
                string := string copyFrom: 4.
                result := result, group, ','
        ].
        string size > 0 ifTrue: [
                result := result, string
        ] ifFalse: [
                result := result copyFrom: 1 to: result size - 1
        ].
        ^result reverse! !
!String categoriesFor: #numericSeparateWithCommas!public! !

"End of package definition"!

"Source Globals"!

"Classes"!

Money guid: (GUID fromString: '{8FF4AD7A-9A93-441C-9104-EDB629BBA700}')!
Money comment: ''!
!Money categoriesForClass!Magnitude-Numbers! !
!Money methodsFor!

currencyCharacter
        ^(self currencySymbol asString) first
!

currencySymbol
        currencySymbol isNil ifTrue: [currencySymbol:= Locale default currency].
        ^currencySymbol
!

currencySymbol: anObject
        currencySymbol:= anObject
!

displayOn: target
        "Append, to the <puttableStream>, target, a String whose chararacters are a
        representation of the receiver as a user would want to see it."

        | stream string split wholePart fractionPart signPrefix |
        stream:= ReadWriteStream on: String new.
        super displayOn: stream.
        string:= stream contents.
        split:= string splitBeforeAndAfter: $..
        wholePart := split first.
        fractionPart := split last.
        wholePart first = $- ifTrue: [ signPrefix:= '-'.
                                                wholePart:= wholePart copyFrom: 2]
                                 ifFalse: [ signPrefix := ''].
        target nextPutAll: signPrefix,
                                self currencySymbol,
                                wholePart numericSeparateWithCommas,
                                '.',
                                fractionPart
!

fromString: aString
        "Parse the string and return a <Money> instance.  Return nil if a problem."
       
        | parseString sign decimals|
        parseString:= aString.

        "Check for a sign"
        sign:= 1.
        parseString first == $- ifTrue: [ sign := -1.
                                                  parseString:= parseString copyFrom: 2 ].

        "Remove an initial currency symbol"
        parseString first = self currencyCharacter ifTrue: [parseString := parseString copyFrom: 2].
       
        "Remove commas"
        parseString := parseString select: [:elem| elem ~~ $,].

        "Only digits and decimal point"
" parseString := parseString select: [:elem| elem between: $0 and: $9 || elem = $. ]."

        "Check for a max 2 decimals"
        (parseString includes: $.)
                ifTrue: [  decimals:= parseString size - (parseString indexOf: $. ).
                            (decimals > 2)
                                ifTrue: [parseString:= parseString copyFrom: 1 to: parseString size - (decimals - 2)  ] ]
                ifFalse: [parseString:= parseString , '.0' ].

        ^Money new: parseString asNumber!

printOn: target
        "Append, to the <puttableStream>, target, a string whose characters are a
        the same as those which would result from sending a #printString
        message to the receiver."

        self displayOn: target.
" target nextPut: $s    - as in ScalesDecimal but this line removed "
! !
!Money categoriesFor: #currencyCharacter!public! !
!Money categoriesFor: #currencySymbol!public! !
!Money categoriesFor: #currencySymbol:!public! !
!Money categoriesFor: #displayOn:!public! !
!Money categoriesFor: #fromString:!public! !
!Money categoriesFor: #printOn:!public! !

!Money class methodsFor!

fromString: aString
        "Parse the string and return a <Currency> instance.  Return nil if a problem."
       
        ^self zero fromString: aString
!

new
        "Answer a new instance of me  ( eg x= Money new (0.00)  )"

        ^self new: 0.00
!

new: aNumber
        "Answer a new instance of me  ( eg x= Money newFrom: 0.09  )"

        ^self basicNew
                setFraction: aNumber asFraction scale: 2;
                yourself
! !
!Money class categoriesFor: #fromString:!public! !
!Money class categoriesFor: #new!public! !
!Money class categoriesFor: #new:!public! !

MoneyPresenter guid: (GUID fromString: '{90E75E00-DD93-11D4-9567-D258DE47A324}')!
MoneyPresenter comment: ''!
!MoneyPresenter categoriesForClass!Unclassified! !
!MoneyPresenter class methodsFor!

defaultModel
        "Answer a default model to be assigned to the receiver when it
        is initialized."

        ^0.00 asMoney ! !
!MoneyPresenter class categoriesFor: #defaultModel!public! !

MoneyToText guid: (GUID fromString: '{8BA0AF6C-C0B8-4723-8EB7-8BE6B8E00670}')!
MoneyToText comment: ''!
!MoneyToText categoriesForClass!MVP-Type Converters-Text! !
!MoneyToText methodsFor!

leftToRight: amount
        "Answers the result of converting amount (of money) to a String"
        ^amount displayString.
!

rightToLeft: aString
        "Answers the result of converting  to a String  to amount (of money)"
        ^Money fromString: aString
! !
!MoneyToText categoriesFor: #leftToRight:!public! !
!MoneyToText categoriesFor: #rightToLeft:!public! !

"Binary Globals"!

"Resources"!

(ResourceIdentifier class: MoneyPresenter name: 'Default view') assign: (Object fromBinaryStoreBytes:
(ByteArray fromHexString: '2153544220312046020C0001000000566965775265736F75726365000000000E0124005354425265736F757263655354424279746541727261794163636573736F7250726F7879000000007200000062020000215354422031204E080C000A0000005354425669657750726F7879000000009A000000000000005200000010000000446F6C7068696E204D565020426173655200000008000000546578744564697462000000100000000000000000000000620000000200000082000000040000000000014401040000A001000000000000000000000000000007000000000000000000000000000000A00100000000000082000000080000006E3835800000000006030B004D6F6E6579546F5465787400000000000000005200000000000000000000000100000006010F004D65737361676553657175656E636500000000CA00000000000000D0000000620000000300000006030B004D65737361676553656E6400000000BA00000000000000520000001000000063726561746541743A657874656E743A620000000200000006020500506F696E74000000000B0000000B000000E202000000000000210100003F000000A00100009202000000000000BA00000000000000520000000F00000073656C656374696F6E52616E67653A620000000100000006030800496E74657276616C00000000030000000100000003000000A00100009202000000000000BA00000000000000520000000F0000006973546578744D6F6469666965643A620000000100000020000000A001000006010F0057494E444F57504C4143454D454E5400000000720000002C0000002C0000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF05000000050000009500000024000000CA00000000000000D00000006200000000000000E202000000000000F1000000F10000000000000013000000460504000300000049636F6E0000000000000000100000000E02110053544253696E676C65746F6E50726F7879000000009A000000000000005200000007000000446F6C7068696E5200000018000000496D61676552656C617469766546696C654C6F6361746F72BA00000000000000520000000700000063757272656E74520000000C00000054657874456469742E69636F0E021F0053544245787465726E616C5265736F757263654C69627261727950726F7879000000005200000010000000646F6C7068696E64723030352E646C6C00000000'))!