how to round multiply in firebird - rounding

table:
var1 | var2
-----+------
0.5 | 19.99
var1 and var2 is double precision type
I know, that saved value could be 19.9900000000000000000000000001 or 19.989999999999999999999999999999999 (or 0.5 is not exact), but how to get the correct value (If the fractional part is exactly 0.5, rounding is upward for positive numbers and downward for negative numbers. - which comes from round definition):
SELECT var1, var2, round(var1*var2,2), round(0.5*19.99,2), round(9.995,2) FROM table
returns
var1 | var2 | this should be 10 | this is 10 | this is also 10
| | round(var1*var2,2)| round(0.5*19.99,2) | round(9.995,2)
-----+-------+-------------------+--------------------+-----------------
0.5 | 19.99 | 9.99 | 10 | 10
I tried round(round(var1,2)*round(var2,2)) but nothing changed

Literals like 0.5 and 19.99 are NUMERIC(18, 1) and NUMERIC(18, 2) respectively. The literal 0.5 * 19.99 is a NUMERIC(18, 3), which means the resulting value is exactly 9.995, which when rounded to 2 decimals will result in a NUMERIC(18, 3) value of 10.000.
On the other hand the same operations on double precision values are not precise. For example:
select
val1 * val2,
cast(val1 * val2 as varchar(100)),
round(val1 * val2, 2),
round(val1 * val2, 3),
round(round(val1 * val2, 3), 2),
round(val1 * val2 + 1e-10, 2)
from (
select
cast(0.5 as double precision) as val1,
cast(19.99 as double precision) as val2
from rdb$database) a
Will yield (in flamerobin), respectively:
9.995000 9.994999999999999 9.990000 9.995000 10.000000 10.000000
Where the first column being 9.995000 is a result of the rendering by flamerobin with max 6 decimals (as shown by the second column).
This may also hint at a solution: first round to a higher number of decimals, and then lower, or add a minor fraction like 1e-10, however this can still yield incorrect rounding with other values.
The same goes for converting the result to fixed point decimals: you will potentially introduce rounding errors somewhere.

the solution for now:
SELECT ROUND(CAST(var1 AS DECIMAL(9,2))*CAST(var2 AS DECIMAL(9,2)),2) FROM table
but maybe someone has better solution

Related

Format the total so it does not show decimal places

So, I'm trying to make this code not show any decimal places in the end (total)... But I can't really find a way to do it. Is there a way to do it without having to rewrite everything?
test1_weight = float(input("Type your 1st Test weight: "))
test2_grade = float(input("Type your 1st Test grade: "))
test2_weight = float(input("Type your 1st Test weight: "))
total = (test1_grade * test1_weight + test2_grade * test2_weight)/(test1_weight + test2_weight)
print ("The weighted average is: ", total)
You can cast total to an integer:
total = int(total)
For example:
total = 3.75
total = int(total)
print(total) # 3
Since each of your inputs are floats, your 'total' will also be a float. If you want the 'total' to not have any decimal points, you should cast total to an integer before printing: int(total)
You can also round the result with the round function
Exemple:
round(total)
You can accomplish this with f-strings. F-strings allow you to interpolate python expressions with strings, meaning you can stick your variable right in there:
>>> total = 3.75
>>> print(f"The total is: {total}")
The total is: 3.75
It also allows for formatting syntax, which means we can restrict the float to no decimal places by specifying the float format .0f after a colon.
>>> total = 3.75
>>> print(f"The total is: {total:.0f}")
The total is: 4

Excel Sumproduct counts rows incorrectly with multiple column criteria

I am trying to calculate Precision and Recall for the output of a model which makes 3 predictions whether a person's name is John or not.
The ground truth for each entry/row is column A. The predictions are stored in columns O,Q and S. The model only needs 2 out of 3 predictions to be > 50% each to be considered correct.
Therefore a True Positive is when >=2 of O,Q,S are > 50%.
Similarly, a False Negative is when < 2 of O,Q,S are > 50%.
Precision = TP / (TP + FP)
Recall = TP / (TP + FN)
I can calculate precision ok because the final logic operator is >= and therefore the values cannot be 0 to be counted. But for this, the final SUM in the denominator is problematic, counting all rows.
This is the part that works, the Precision:
SUM(IF((A2:A300="John")*((O2:O300>=.5)+(Q2:Q300>=.5)+(S2:S300>=.5))>=2,1)) / (SUM(IF((A2:A300="John")*((O2:O300>=.5)+(Q2:Q300>=.5)+(S2:S300>=.5))>=2,1)) + (SUM(IF((A2:A300="Not John")*((O2:O300>=.5)+(Q2:Q300>=.5)+(S2:S300>=.5))>=2,1)))
And this is what I'm trying but doesn't work. The last < operator screws up the denominator and I can't figure out how to fix:
SUM(IF((A2:A300="John")*((O2:O300>=.5)+(Q2:Q300>=.5)+(S2:S300>=.5))>=2,1)) / (SUM(IF((A2:A300="John")*((O2:O300>=.5)+(Q2:Q300>=.5)+(S2:S300>=.5))>=2,1)) + (SUM(IF((A2:A300="John")*((O2:O300>=.5)+(Q2:Q300>=.5)+(S2:S300>=.5))<2,1)))
If there are 3 rows where A = "John" with only 2 of those rows have 2 of O,Q,S > 50%
And if there are 3 rows where A = "Not John" with all 3 rows have O,Q,S > 50%
Then,
Precision = 2 / (2 + 3) = 2/5
Recall = 2 / (2 + 1) = 2/3
You can fix the recall by putting an extra set of brackets round the <2 comparison:
=SUM(IF((A2:A7="John")*((O2:O7>=0.5)+(Q2:Q7>=0.5)+(S2:S7>=0.5))>=2,1))/(SUM(IF((A2:A7="John")*((O2:O7>=0.5)+(Q2:Q7>=0.5)+(S2:S7>=0.5))>=2,1))+(SUM(IF((A2:A7="John")*(((O2:O7>=0.5)+(Q2:Q7>=0.5)+(S2:S7>=0.5))<2),1))))
so you avoid multiplying by zero for the 'Not John' rows before doing the comparison and get the correct denominator.

Replace Number that falls Between Two Values (Pandas,Python3)

Simple Question Here:
b = 8143.1795845088482
d = 14723.523658084257
My Df called final:
Words score
This 90374.98788
is 80559.4495
a 43269.67002
sample 34535.01172
output Very Low
I want to replace all the scores with either 'very low', 'low', 'medium', or 'high' based on whether they fall between quartile ranges.
something like this works:
final['score'][final['score'] <= b] = 'Very Low' #This is shown in the example above
but when I try to play this immediately after it doesn't work:
final['score'][final['score'] >= b] and final['score'][final['score'] <= d] = 'Low'
This gives me the error: cannot assign operator. Anyone know what I am missing?
Firstly you must use the bitwise (e.g. &, | instead of and , or) operators as you are comparing arrays and therefore all the values and not a single value (it becomes ambiguoous to compare arrays like this plus you cannot override the global and operator to behave like you want), secondly you must use parentheses around multiple conditions due to operator precendence.
Finally you are performing chain indexing which may or may not work and will raise a warning, to set your column value use loc like this:
In [4]:
b = 25
d = 50
final.loc[(final['score'] >= b) & (final['score'] <= d), 'score'] = 'Low'
final
Out[4]:
Words score
0 This 10
1 is Low
2 for Low
3 You 704
If your DataFrame's scores were all floats,
In [234]: df
Out[234]:
Words score
0 This 90374.98788
1 is 80559.44950
2 a 43269.67002
3 sample 34535.01172
then you could use pd.qcut to categorize each value by its quartile:
In [236]: df['quartile'] = pd.qcut(df['score'], q=4, labels=['very low', 'low', 'medium', 'high'])
In [237]: df
Out[237]:
Words score quartile
0 This 90374.98788 high
1 is 80559.44950 medium
2 a 43269.67002 low
3 sample 34535.01172 very low
DataFrame columns have a dtype. When the values are all floats, then it has a float dtype, which can be very fast for numerical calculations. When the values are a mixture of floats and strings then the dtype is object, which mean each value is a Python object. While this gives the values a lot of flexibility, it is also very slow since every operation ultimately resorts back to calling a Python function instead of a NumPy/Panda C/Fortran/Cython function. Thus you should try to avoid mixing floats and strings in a single column.

Put Last Two Digits Behind Decimal

I have a column in SQL that uses an integer data type. What is the best way to get the values from the column where the last two digits of the integer are a decimal. These results would need to be displayed in a new column.
Example:
COL_1 Col_2
25489 -> 254.89
489196 -> 4891.96
250 -> 2.50
77 -> .77
It's easier than you think. Simply cast the value as a float and multiply it by 0.01. that will create a new value with the decimal two over from the right.
You just need to divide it by 100:
UPDATE my_table
SET col2 = col1 / 100.0

Google Spreadsheet - Round down at 0.5, but round up above that

The regular "ROUND" function will round down when < 0.5, and will round up when >= 0.5
I need 0.5 to be rounded down, but anything above that to be rounded up.
So:
10.4 should be 10
10.5 should be 10
10.6 should be 11
Edit: Here is the solution i came up with
If the value to be rounded is in B1
And the decimal precision is in A1 (0 = no decimals, 1 = one decimal place, etc)
=IF(MOD(ABS(B1),(1/(10^A1)))<=0.5*(1/(10^A1)),ROUNDDOWN(B1,A1),ROUNDUP(B1,A1))
The ABS() makes sure it works with negative numbers.
The (1/(10^A1)) makes sure that my precision (which is a second argument to Google's rounding functions) scales my boundary condition (0.5) accordingly.
And the MOD() is what actually determines my boundary condition.
Edit2:
More elegant solution thanks to #Jayen
=ROUNDUP(B1 - sign(B1) * (10 ^ -A1) / 2, A1)
It is possible to create an IF statement that would do this =IF(A1-int(A1)>0.5,int(A1)+1,int(A1))
But seams a strange request, the normal convention (in the west) is to round .5 up, not down.
Warning: this credited 'solution' has a bug:
=ROUNDUP(B1 - sign(B1) * (10 ^ -A1) / 2, A1)
Plug in .1, .2, .3, .4 or negative values of those to see the unintended results. The solution i went with is:
=ROUNDUP(MAX(ABS(B1)-1/2,0))*SIGN(B1)
So i used Jayen's clever formula, but used MAX with the ABS to eliminate the bug, then multiplied by the SIGN to allow it to work for positive and negative numbers.
I'm creating C1 = (10 ^ -A1) / 2 which is 0.5 if you round to 0 decimal places, 0.05 if you round to 1, etc.
Then it is simply:
=ROUNDUP(B1 - C1, A1)
Or substituting:
=ROUNDUP(B1 - (10 ^ -A1) / 2, A1)
EDIT:
Not sure if you want negative half numbers to round towards or away from 0, but is this ok?
=ROUNDUP(B1 - sign(B1) * (10 ^ -A1) / 2, A1)
That would be rounding towards 0 on the half.
EDIT2:
But in case you want negative half numbers to round away from 0 (all half numbers round towards negative infinity):
=CEILING(B1 - 10 ^ -A1 / 2, 10 ^ -A1)
EDIT3:
#ShawnKovac found an issue with my original answer when B1 < C1, so I've taken his and adapted it for any A1:
=ROUNDUP(MAX(ABS(B1) - 10 ^ -A1 / 2, 0), A1) * SIGN(B1)
Also my answer for rounding towards negative infinity throws an error when B1 < C1, so here's an update:
=CEILING(B1 - 10 ^ -A1 / 2, SIGN(B1 - 10 ^ -A1 / 2) * 10 ^ -A1)
Depending on the dataset, you could just subtract from your value, so long as you know that none of your numbers would be invalidated by doing so.
=round(A1 - 0.00001)
=IF(AND(LEN(MOD(A2,1)>4),RIGHT(A2,1)="5"),ROUNDDOWN(A2,2),ROUND(A2,2))
The mod 1 will leave just the 0. and the decimal places. So if 3 decimal places and you want to round to 2, and the decimal has a length greater than 4 ("0." plus decimals), this would indicate the decimal needs rounding. If the last digit of the decimal is 5 then use the rounddown feature else use round.
well if you just subtract 0.1 it will work with positive but if you use negative you can use if A1 is lower then 0 add 0.1
IF(A1<0,ROUND(A1+0.1,0),ROUND(A1-0.1,0))
A Table to show the result of the formula above ^^^^^
Hope it helped :)

Resources