IMPORTANT ANNOUNCEMENT

These forums were permanently set to read-only mode on July 20, 2022. From that day onwards, no new posting or comment is allowed on the site, but the historical content remains intact and searchable.

A new location for posting questions about PlanetPress Suite is now available:

OL Learn - PlanetPress Classic (opens in new tab)

Topic Options
#33541 - 10/07/10 06:18 PM Float Multiply Results
xss Offline
OL Toddler

Registered: 04/21/10
Posts: 34
Loc: Vancouver
I tried to multiply float data A (6 decimals) to float data B (3 decimals). I used strtofloat to get data and after multiply, floattostr to display. It works fine except the result is not exact the amount it supposed to be (see below example). Could this be a rounding error? Is there a way to fix it? Thanks!

0.219011 x 11111.000 = 2433.431221

My result is 2433.431152

Top
#33544 - 10/08/10 09:18 AM Re: Float Multiply Results [Re: xss]
Raphael Lalonde Lefebvre Offline
OL Expert

Registered: 10/14/05
Posts: 4956
Loc: Objectif Lune Montreal
xss,

For some reasons, using the numbers you provided, I get:

2433.431

It's not displaying the digits 221 or 152, it rounds at three digits. Can you post the code you've used to obtain 2433.431221? Because with only using the floattostr and strtofloat commands, I get 2433.431.

Thanks!

Raphaël

Top
#33548 - 10/08/10 12:58 PM Re: Float Multiply Results [Re: Raphael Lalonde Lefebvre]
xss Offline
OL Toddler

Registered: 04/21/10
Posts: 34
Loc: Vancouver
Did you spcifiy the number of decimal places to include in the result string - like floattostr(&x, 6)? I understand Planet Press can handle 8 decimals that's why so confused about the result...

Besides, if I set 8 decimals as floattostr(&x, 8), I got result 2433.43115234...Not sure where the last server digits came from. I even used function (mul(&y, &z))instead of (&y * &z), but got the same result frown

Again, I feel something wrong with strtofloat and floattostr. The number I have is 0.219011, but after strtofloat and floattostr(&y, 8), it shows 0.21901099...


Edited by xss (10/08/10 01:50 PM)

Top
#33555 - 10/11/10 10:14 AM Re: Float Multiply Results [Re: xss]
stuartg Offline
OL Expert

Registered: 03/06/03
Posts: 713
Loc: Swindon, England
You need to understand that floating point numbers (also known as reals or measures) are not designed to hold exact values. It is characteristic of their use that converting to a float and converting back again only gives you an answer that matches within the precision of the float variable.

In postscript, floats are typically (it apparently depends on the implementation) held as 32 bit values. Many implementations will conform to the IEEE 754-2008 binary-32 standard. This gives a 8 bit exponent and a 24 bit mantissa. These are binary values.
Converting to decimal, the exponent goes to 10^38.23 and the mantiassa gives 7.2 decimal digits.

This means that you cannot expect accuracy of more that 7 significant figures when doing arithmetic on floats.

In your example 0.219011 x 11111.000 = 2433.431 is the best you can hope for. If you require higher precision you ought to do the sums externally to PlanetPress and import the results with your data.

Hope that helps

Top
#33559 - 10/12/10 11:29 AM Re: Float Multiply Results [Re: stuartg]
xss Offline
OL Toddler

Registered: 04/21/10
Posts: 34
Loc: Vancouver
Thanks a lot for your explanation, stuartg!

Now I understand that I have to seek solution outside of Planet Press...

Top
#33562 - 10/12/10 03:06 PM Re: Float Multiply Results [Re: xss]
Raphael Lalonde Lefebvre Offline
OL Expert

Registered: 10/14/05
Posts: 4956
Loc: Objectif Lune Montreal
xss,

I actually took up the challenge of writing a function that will allow us to multiply two floats together with the proper precision, and I believe I got it to work pretty well!

What it does is that it simulates manual multiplications, as if you were multiplying two numbers manually on paper. The good old method that we no longer use ever since calculators and computers made us lazy! wink

First, you will need to create a global variable and call it "arrayresults" (change both the display name and presstalk ID), make it type "Array - string", and set it's size to 100 or higher.

Then, create a global function, and copy-pages the huge code below:

function @LongMultiply(&ns1:string, &ns2:string, &numdecimals:integer):string
% -----------------------------
% Function: @LongMultiply
% By: Raphaël Lalonde Lefebvre
%
% This function multiplies two decimal numbers, passed as strings. Sometimes,
% the multiplication doesn't provide an exact result when using floattostr or
% strtofloat. Past a certain point, the precision is altered and becomes wrong.
% This function is a workaround to this issue.
%
% The other use is to multiply BIG numbers. You normally cannot multiply
% billions together. This function can be used for that!
%
% Parameters: &ns1: First number to multiply. It's actually a string.
% &ns2: Second number to multiply.
% &numdecimals: Specific number of decimals. If set to -1, it is
% ignored.
%
% The result is a floating point number, unless two integers are multiplied, then
% the result will be an integer. The result is actually returned as a String.
%
% You can increase the size of the &arrayresults array to handle extremely large
% numbers.
%
% NOTE THAT THIS FUNCTION IS NOT OFFICIALLY SUPPORTED BY OBJECTIF LUNE.
% IT IS PROVIDED "AS-IS", WITH NO GUARANTEES OF IT'S FUNCTIONALITY OR PERFORMANCE.
% -----------------------------
define(&i, integer, 0)
define(&j, integer, 0)
define(&k, integer, 0)
define(&l, integer, 0)
define(&r, integer, 0)
define(&res, string, '')
define(&i1, integer, 0)
define(&i2, integer, 0)
define(&s, string, '')
define(&total, string, '')
define(&decimals, integer, 0)
define(&largest, integer, 0)
define(&sum, integer, 0)
define(&Zeroes, integer, 0)
define(&Negative, boolean, False)

% Negative or positive?
if(((pos('-', &ns1) > 0) and (pos('-', &ns2) > 0)) or ((pos('-', &ns1) = 0) and (pos('-', &ns2) = 0)))
&Negative := False
elseif()
&Negative := True
endif()

% Calculate the number of digits after the decimals.
if(pos('.', &ns1) > 0)
&decimals := &decimals + (length(&ns1) - pos('.', &ns1))
endif()
if(pos('.', &ns2) > 0)
&decimals := &decimals + (length(&ns2) - pos('.', &ns2))
endif()

&ns1 := strip('.', &ns1)
&ns2 := strip('.', &ns2)

for(&i, length(&ns2), -1, 1)
&r := 0
&s := '' + left('00000000000000000000', &k)
for(&j, length(&ns1), -1, 1)
&i1 := strtoint(mid(&ns1, &j, 1))
&i2 := strtoint(mid(&ns2, &i, 1))

if((&i2 * &i1) > 9)
&s := inttostr(strtoint(mid(inttostr((&i2 * &i1)+&r), length(inttostr((&i2 * &i1)+&r)),1)) ) + &s
&r := strtoint(mid(inttostr((&i2 * &i1)+&r), length(inttostr((&i2 * &i1)+&r))-1,1))
elseif()
&s := inttostr((&i2 * &i1) + &r) + &s
&r := 0
endif()
endfor()

if(&r > 0)
&arrayresults[&k] := inttostr(&r) + &s
elseif()
&arrayresults[&k] := &s
endif()
&k := &k + 1
endfor()

% Add up everything.
&total := ''
&largest := length(&arrayresults[&k-1])

% Add zeroes.
for(&i, 0, 1, &k-1)
if(length(&arrayresults[&i]) < &largest)
&arrayresults[&i] := left('00000000000000000000', &largest - length(&arrayresults[&i])) + &arrayresults[&i]
endif()
endfor()

&total := ''
&r := 0
for(&i, &largest, -1, 1)
&sum := 0 + &r
for(&j, (&k-1), -1, 0)
&sum := &sum + strtoint(mid(&arrayresults[&j], &i, 1))
endfor()
if(&sum > 9)
&total := mid(inttostr(&sum), length(inttostr(&sum)), 1) + &total
&r := strtoint(mid(inttostr(&sum), length(inttostr(&sum))-1, 1))
elseif()
&total := inttostr(&sum) + &total
&r := 0
endif()
endfor()

% Add the decimal point.
&res := ''
if(&decimals > 0)
&j := 0
for(&i,length(&total), -1, 1)
&res := mid(&total, &i, 1) + &res
&j := &j + 1
if(&j = &decimals)
&res := '.' + &res
endif()
endfor()
elseif()
&res := &total
endif()

% Remove leading zeroes.
for(&i, 1, 1, length(&res))
if(mid(&res, &i, 1) = '0')
&Zeroes := &Zeroes + 1
elseif()
&res := mid(&res, &Zeroes+1, length(&res) - (&Zeroes))
exit()
endif()
endfor()

% Put a zero if the '.' is the first thing in the string.
if(mid(&res, 1, 1) = '.')
&res := '0' + &res
endif()

% Reset Zeroes counter.
&Zeroes := 0

% Remove trailing zeroes.
% Only do this if it's a floating point number.
if(pos('.', &res) > 0)
for(&i, length(&res), -1, 1)
if(mid(&res, &i, 1) = '0')
&Zeroes := &Zeroes + 1
elseif()
&res := mid(&res, 1, length(&res) - (&Zeroes-1))
exit()
endif()
endfor()

% Add one last zero if the last character is a '.'.
if(mid(&res, length(&res), 1) = '.')
&res := &res + '0'
endif()
endif()

% Add the negative sign if needed.
if(&Negative)
&res := '-' + &res
endif()

% Minimum and maximum number of decimals.
if(&numdecimals > -1)
if(pos('.', &res) > 0)
&i := length(mid(&res, pos('.', &res)+1, length(&res) - pos('.', &res)))
% Minimum.
if(&i < &numdecimals)
&res := &res + left('0000000000000000000000000000000000000000', &numdecimals - &i)
endif()
% Maximum.
if(&i > &numdecimals)
&res := mid(&res, 1, pos('.', &res)-1) + '.' + mid(&res, pos('.', &res)+1, &numdecimals)
endif()
% If it's zero, remove the '.'.
if(&numdecimals = 0)
&res := strip('.', &res)
endif()
elseif()
if(&numdecimals > 0)
&res := &res + '.' + left('0000000000000000000000000000000000000000', &numdecimals)
endif()
endif()
endif()

&Result := &res
endfunction()



Then, you can call this function in a data selection. Example:

=@LongMultiply('0.219011', '11111', -1)

And that will give us the 2433.431221 that we're looking for. The "-1" is the number of decimals. By specifying -1, it means that there is no specified number of decimals. You can set it to the number of decimals that you want to display. Note that the function expects strings as the parameters, so you can use data selections as the parameters. Do not use strtofloat. Here's an example what it may look like:

=@LongMultiply(@(1,1,20), @(2,1,20), -1)

Note that this function is not an official solution, and therefore is not supported by Objectif Lune. It is provided as-is, with no guarantees of perfect functionality.

That said, I've tested it with various numbers, both entire numbers, floating points and a mix of both, and it has been working fine so far!

Hope that helps!

Regards,
Raphaël Lalonde Lefebvre


Edited by Raphael Lalonde Lefebvre (10/13/10 10:16 AM)

Top
#33568 - 10/12/10 07:37 PM Re: Float Multiply Results [Re: Raphael Lalonde Lefebvre]
xss Offline
OL Toddler

Registered: 04/21/10
Posts: 34
Loc: Vancouver
Hi Raphaël,

The function is great! I got all correct results. The only thing is if I can set how many decimals I want? I set "arrayresults" as 20, and I have 7 decimals instead of 6 such as 2433.4312210...

Thank you!

Top
#33574 - 10/13/10 10:18 AM Re: Float Multiply Results [Re: xss]
Raphael Lalonde Lefebvre Offline
OL Expert

Registered: 10/14/05
Posts: 4956
Loc: Objectif Lune Montreal
xss,

I have just edited my post above, with an extra parameter to specify the number of decimals that you want. You can set it to 7 is you want 7 decimals. Set it to -1 if you don't want any specific number of decimals. If you set it to 0, it will also remove the '.'.

Regards,
Raphaël Lalonde Lefebvre

Top
#33577 - 10/13/10 12:12 PM Re: Float Multiply Results [Re: Raphael Lalonde Lefebvre]
xss Offline
OL Toddler

Registered: 04/21/10
Posts: 34
Loc: Vancouver
You are awesome, Raphaël!

I have the decimals I want. Thank you!

Top