Display double in 2 decimal places in RPG? - rpgle

When declaring floating-point variable, I don't understand the amount of precision needed for the stored object in RPG ??
In my exercice, I enter the number of copies for example the value 5.
The total amount is 0.50 euros but I have like message:
I don't understand how to declare the variable total correctly in double.
H
D NumberCopy S 3S 0
D Total S ???
*
/Free
dsply 'Enter your copy number please : ' '' NumberCopy;
If (NumberCopy < 11);
Total = NumberCopy * 0.10;
ElseIf (NumberCopy < 31);
Total = (10 * 0.10) + (NumberCopy - 10) * 0.09;
Else;
Total = (10 * 0.10) + (20 * 0.09) + (NumberCopy - 30) * 0.08;
EndIf;
dsply ('The amount is of ' + %Char(Total) + ' euros');
*inlr = *on;
/End-Free
Here I find this on RPGPDM.
D Float1 S 8F
https://www.rpgpgm.com/2014/02/defining-variables-in-rpg-all-free.html

You have declared Float1 as a "double". But the problem is that floating point numbers are not a good choice for fixed decimal precision numbers. See https://en.wikipedia.org/wiki/Floating-point_arithmetic#Accuracy_problems .
You should use packed or zoned if you are working with numbers where correct precision is needed and not just some approximation.

to say it at the beginning of your exercise DONT USE FLOAT IN RPG. It will make you pretty unhappy. To answer your question: dcl-s total float(8); You have two problems with float:
You dont want your customers to get wrong invoices due to accuracy problems like in 1990.
Decimals are able too use hardware support to evaluate faster which isn't "important" but a nice to have.
If you wanna display a float you can do something like that
dcl-s outputString char(10) inz;
dcl-s total float(8) inz (5);
outputString = %char(%dec(total: 10: 2));
dsply outputString;
Some hints:
Don't use /free or /end-free the compiler is able to acknowledge tht on its own.
Don't use fixed format declarations to define your fields. They are harder to read and there is no actual reason to use them.

Related

How to get first 4 digits after the 0s in a decimal but keeping 0s • Python

I need the first 4 digits after the front 0s in a decimal while also keeping the 0s in the output without getting scientific notation.
Would like to take
0.0000000000000000000000634546534
and get
0.00000000000000000000006345 but not
6.345e-23.
But would also like to take
231.00942353246
and get
231.009423.
Thank you.
Here's one way using the decimal module.
import decimal
example = 0.0000000000000000000000634546534
x = decimal.Decimal(example)
sign = x.as_tuple().sign
digits = x.as_tuple().digits
exponent = x.as_tuple().exponent
figs = 4
result = decimal.Decimal((sign, digits[:figs], len(digits)+(exponent)-figs))
precision = -1 * (len(digits) + (exponent) - figs) # for this example: -1 * (99 + (-121) - 4)
print("{:.{precision}f}".format(float(result), precision=precision))
Result:
0.00000000000000000000006345
Note that Decimal stores 99 digits because of floating point imprecision. The example variable holds a float value (due to the initializer value) that is inherently imprecise. There is no way around this, unless you are able to represent the original float value as a string, that you can use to initialize the example variable.
There are cases, where the 4th digit shown will be wrong, because in the floating point representation that digit is represented as one lesser and the next digit is a 9, for example. To fix, this, we grab one more digit as well to use for rounding. This should work in most cases, as the imprecision should be within the nearest rounding threshold.
result = decimal.Decimal((0, digits[:figs + 1], len(digits)+(exponent)-figs-1))
Lastly, to handle the case where there are numbers before the decimal, we can simply store it, remove it, and re-add it:
whole_number_part = int(example)
example -= whole_number_part
...
result += whole_number_part
Altogether, we get:
import decimal
example = 231.00942353246
whole_number_part = int(example)
example -= whole_number_part
x = decimal.Decimal(example)
sign = x.as_tuple().sign
digits = x.as_tuple().digits
exponent = x.as_tuple().exponent
figs = 4
result = decimal.Decimal((0, digits[:figs + 1], len(digits)+(exponent)-figs-1))
result += whole_number_part
precision = -1 * (len(digits) + (exponent) - figs)
print("{:.{precision}f}".format(float(result), precision=precision))
Result:
231.009423

Strange result from Summation of numbers in Excel and Matlab [duplicate]

I am writing a program where I need to delete duplicate points stored in a matrix. The problem is that when it comes to check whether those points are in the matrix, MATLAB can't recognize them in the matrix although they exist.
In the following code, intersections function gets the intersection points:
[points(:,1), points(:,2)] = intersections(...
obj.modifiedVGVertices(1,:), obj.modifiedVGVertices(2,:), ...
[vertex1(1) vertex2(1)], [vertex1(2) vertex2(2)]);
The result:
>> points
points =
12.0000 15.0000
33.0000 24.0000
33.0000 24.0000
>> vertex1
vertex1 =
12
15
>> vertex2
vertex2 =
33
24
Two points (vertex1 and vertex2) should be eliminated from the result. It should be done by the below commands:
points = points((points(:,1) ~= vertex1(1)) | (points(:,2) ~= vertex1(2)), :);
points = points((points(:,1) ~= vertex2(1)) | (points(:,2) ~= vertex2(2)), :);
After doing that, we have this unexpected outcome:
>> points
points =
33.0000 24.0000
The outcome should be an empty matrix. As you can see, the first (or second?) pair of [33.0000 24.0000] has been eliminated, but not the second one.
Then I checked these two expressions:
>> points(1) ~= vertex2(1)
ans =
0
>> points(2) ~= vertex2(2)
ans =
1 % <-- It means 24.0000 is not equal to 24.0000?
What is the problem?
More surprisingly, I made a new script that has only these commands:
points = [12.0000 15.0000
33.0000 24.0000
33.0000 24.0000];
vertex1 = [12 ; 15];
vertex2 = [33 ; 24];
points = points((points(:,1) ~= vertex1(1)) | (points(:,2) ~= vertex1(2)), :);
points = points((points(:,1) ~= vertex2(1)) | (points(:,2) ~= vertex2(2)), :);
The result as expected:
>> points
points =
Empty matrix: 0-by-2
The problem you're having relates to how floating-point numbers are represented on a computer. A more detailed discussion of floating-point representations appears towards the end of my answer (The "Floating-point representation" section). The TL;DR version: because computers have finite amounts of memory, numbers can only be represented with finite precision. Thus, the accuracy of floating-point numbers is limited to a certain number of decimal places (about 16 significant digits for double-precision values, the default used in MATLAB).
Actual vs. displayed precision
Now to address the specific example in the question... while 24.0000 and 24.0000 are displayed in the same manner, it turns out that they actually differ by very small decimal amounts in this case. You don't see it because MATLAB only displays 4 significant digits by default, keeping the overall display neat and tidy. If you want to see the full precision, you should either issue the format long command or view a hexadecimal representation of the number:
>> pi
ans =
3.1416
>> format long
>> pi
ans =
3.141592653589793
>> num2hex(pi)
ans =
400921fb54442d18
Initialized values vs. computed values
Since there are only a finite number of values that can be represented for a floating-point number, it's possible for a computation to result in a value that falls between two of these representations. In such a case, the result has to be rounded off to one of them. This introduces a small machine-precision error. This also means that initializing a value directly or by some computation can give slightly different results. For example, the value 0.1 doesn't have an exact floating-point representation (i.e. it gets slightly rounded off), and so you end up with counter-intuitive results like this due to the way round-off errors accumulate:
>> a=sum([0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1]); % Sum 10 0.1s
>> b=1; % Initialize to 1
>> a == b
ans =
logical
0 % They are unequal!
>> num2hex(a) % Let's check their hex representation to confirm
ans =
3fefffffffffffff
>> num2hex(b)
ans =
3ff0000000000000
How to correctly handle floating-point comparisons
Since floating-point values can differ by very small amounts, any comparisons should be done by checking that the values are within some range (i.e. tolerance) of one another, as opposed to exactly equal to each other. For example:
a = 24;
b = 24.000001;
tolerance = 0.001;
if abs(a-b) < tolerance, disp('Equal!'); end
will display "Equal!".
You could then change your code to something like:
points = points((abs(points(:,1)-vertex1(1)) > tolerance) | ...
(abs(points(:,2)-vertex1(2)) > tolerance),:)
Floating-point representation
A good overview of floating-point numbers (and specifically the IEEE 754 standard for floating-point arithmetic) is What Every Computer Scientist Should Know About Floating-Point Arithmetic by David Goldberg.
A binary floating-point number is actually represented by three integers: a sign bit s, a significand (or coefficient/fraction) b, and an exponent e. For double-precision floating-point format, each number is represented by 64 bits laid out in memory as follows:
The real value can then be found with the following formula:
This format allows for number representations in the range 10^-308 to 10^308. For MATLAB you can get these limits from realmin and realmax:
>> realmin
ans =
2.225073858507201e-308
>> realmax
ans =
1.797693134862316e+308
Since there are a finite number of bits used to represent a floating-point number, there are only so many finite numbers that can be represented within the above given range. Computations will often result in a value that doesn't exactly match one of these finite representations, so the values must be rounded off. These machine-precision errors make themselves evident in different ways, as discussed in the above examples.
In order to better understand these round-off errors it's useful to look at the relative floating-point accuracy provided by the function eps, which quantifies the distance from a given number to the next largest floating-point representation:
>> eps(1)
ans =
2.220446049250313e-16
>> eps(1000)
ans =
1.136868377216160e-13
Notice that the precision is relative to the size of a given number being represented; larger numbers will have larger distances between floating-point representations, and will thus have fewer digits of precision following the decimal point. This can be an important consideration with some calculations. Consider the following example:
>> format long % Display full precision
>> x = rand(1, 10); % Get 10 random values between 0 and 1
>> a = mean(x) % Take the mean
a =
0.587307428244141
>> b = mean(x+10000)-10000 % Take the mean at a different scale, then shift back
b =
0.587307428244458
Note that when we shift the values of x from the range [0 1] to the range [10000 10001], compute a mean, then subtract the mean offset for comparison, we get a value that differs for the last 3 significant digits. This illustrates how an offset or scaling of data can change the accuracy of calculations performed on it, which is something that has to be accounted for with certain problems.
Look at this article: The Perils of Floating Point. Though its examples are in FORTRAN it has sense for virtually any modern programming language, including MATLAB. Your problem (and solution for it) is described in "Safe Comparisons" section.
type
format long g
This command will show the FULL value of the number. It's likely to be something like 24.00000021321 != 24.00000123124
Try writing
0.1 + 0.1 + 0.1 == 0.3.
Warning: You might be surprised about the result!
Maybe the two numbers are really 24.0 and 24.000000001 but you're not seeing all the decimal places.
Check out the Matlab EPS function.
Matlab uses floating point math up to 16 digits of precision (only 5 are displayed).

Why is a whole number not the same when rounded in a custom function?

I have the following custom function that rounds a number to a user-specified accuracy.
It is based on the general formula:
ROUND(Value/ Accuracy,0)*Accuracy
There are times where Number/Accuracy is exactly a multiple of 0.5, and Excel does not do the common rounding rule (ODD number - Round up, EVEN number - Round down), so I made a custom function.
Function CheckTemp(val As Range, NumAccuracy As Range) As Double
Dim Temp As Double
Temp= Abs(val) / NumAccuracy
CheckTemp = (Temp / 0.5) - WorksheetFunction.RoundDown(Temp / 0.5 , 0)
End Function
If CheckTemp = 0, then 'val' falls under this case where depending on the number, I want to specifically round down or up. If it is false, then the general Round() command is used.
I do have a weird case when Accuracy = 0.1 and any 'val' that meets the requirement:
#.X5000000...,
where: 'X' is an ODD number, or zero (i.e. 0,1,3,5,7,9).
Depending on the whole number, the function does not work.
Example:
val = - 5 361 202.55
NumAccuracy = 0.1
Temp = 53 612 025.5
Temp / 0.5 = 107 224 051.
WorksheetFunction.RoundDown(Temp / 0.5,0) = 107 224 051.
CheckTemp = -1.49012E-08
If I break this check into two separate functions, one to output (Temp/0.5) and WF.RoundDown(Temp / 0.5) to the Excel worksheet, and then subtract the two in the worksheet I get EXACTLY 0.
However with VBA coding, an error comes into play and results in a non-zero answer (even more worrisome a NEGATIVE value, which should be impossible when Temp is always positive, and RoundDown('x','y') will always result in a smaller number than 'x').
'val' can be a very large number with many decimal places, so I am trying to keep the 'Double' parameter if possible.
I tried 'Single' variable type and it seems to remove the error with CheckTemp(), but I am worried an end-user may use a number that exceeds the 'Single' variable limit.
You are not wrong, but native rounding in VBA is severely limited.
So, use a proper rounding function like RoundMid as found in my project VBA.Round. It uses Decimal if possible to avoid such errors.
Example:
Value = 5361202.55
NumAccuracy = 0.1
RoundedValue = RoundMid(Value / NumAccuracy, 0) * Numaccuracy
RoundedValue -> 5361202.6

how to generate random numbers between closed interval [0,1]?

I need to get soma random values between closed interval [0,1] rather than opened interval. Is there any way this can be done?
Is this ok?
You can use:
random.uniform(0, 1)
Note: When calling N = random.uniform(a, b), the behaviour is always that a <= N <= b but the end-point value b may or may not be included in the range depending on floating-point rounding.
See https://docs.python.org/3/library/random.html?highlight=uniform#random.uniform
First, try: import random (random.randint(0,10**6)*1.0 /10**6)
This will give you full floating point precision.
Otherwise, try:
import decimal
def randfloat():
decimal.getcontext().prec = 10 # 10 decimal points enough?!
return decimal.Decimal(0) + decimal.Decimal(random.uniform(0, 1))
# this should include both boundaries as float gets close enough to 1 to make decimal round
>>> decimal.Decimal(0) + decimal.Decimal(0.99999999999)
Decimal('1.000000000')
while uniform() apparently guarantees the inclusion of the lower boundary

how to limit the number of digit after the float with python 3?

In my program I have several calculations that produce float numbers as results.
I would like to know if there's a general declaration in Python 3 that allows to limit all the floats in the program to let's say 8 digits, systematically ?
Thank you for your help !
# Create initial balance for user 1 and user 2.
bal_user1 = 21.82233503
bal_user2 = 5.27438039
# Calculate percentage of capital for each user
percent_capi_user2 = 100 * bal_user2 / ( bal_user1 + bal_user2)
percent_capi_user1 = 100 - percent_capi_user2
print("User 1 as " + str(percent_capi_user1) + (" % of the capital"))
print("User 2 as " + str(percent_capi_user2) + (" % of the capital"))
The output is :
User 1 as 80.53498253110413 % of the capital
User 2 as 19.465017468895866 % of the capital
I would like for example : 80.53498253 instead of 80.53498253110413
And since I'm doing several calculations later on in the program, I was wondering if there was a general declaration to put once at the beginning of the code. In order to avoid casting the right number of digits each time...
Well, buddy, I think I have just what you are looking for!
What you are looking for is the decimal module and the included Decimal class. Now, I am not going to go into it, because I am not that knowledgeful in it, but what I can do is point you in the right direction. In short, read the documentation here ( https://docs.python.org/3/library/decimal.html?highlight=decimal#module-decimal ), and look for decimal.getcontext().prec, which will allow you to, at least with Decimal objects, control their precision "globally".

Resources