1

I am comparing prices from a pricelist (multiplied by a percentage) with current price stored in the database.

  • The database price is always stored as decimal.
  • The prices from the pricelist are always read as float or int.

Test:

for recurringFee, plan_rate_id in data:
    for offer_name, pc_offer_id, agreement_type, purchase_unit, secondary_license_type, end_customer_type, pc_list_price,pc_erp_price in price_list:
        price_list_types.add(type(pc_erp_price).__name__)
        pba_db_types.add(type(recurringFee).__name__)
print(price_list_types)
print(pba_db_types)

Output:

{'float', 'int'}
{'Decimal'}

If there is a changes in price, I update the db value to the new price.

If the current price in the db is 0.45 (for example), when I do a select, it is always read/stored as:

0.45000000

I believe the math.isclose() function can help me.

When math.isclose() is false, it is a trigger to:

  1. Print to screen what is the current price (without trailing zeros) and what is the new price (rounded to 2 decimal places).
  2. Write to a csv file.
  3. Append the new value (rounded to 2 decmial places) to a list to be later updated via sql update queries.

Problem is right now it is printing to screen and appending to list real changes and also identical values.

I am printing to screen using the .format()

I tried defining a rel_tol value for math.isclose but I believe the numbers are already being compared properly if I don't specify any rel_tol

>>> fee = 6.51000000
>>> print(erp_price)
6.5068
>>> math.isclose(fee,erp_price)
False
>>> math.isclose(fee,erp_price,rel_tol=0.01)
True
>>> math.isclose(fee,erp_price,rel_tol=0.001)
True
>>> math.isclose(fee,erp_price,rel_tol=0.0001)
False
>>>

Code snippet:

if not(math.isclose(pc_list_price,recurringFee)):
    print("Addons Markup: \n{}:\n{}\nPlan {}:\nYearly: {}\nOwner: {}\nPrice {} -> {}\n".format(res_name,offer_id,plan_id,yearly,owner_id,float(recurringFee),round(pc_list_price,2)))
    writer.writerow([owner_id,currency,plan_id,res_id,plan_rate_id,res_name,offer_id,float(recurringFee),round(pc_list_price,2),value,key])
    to_be_updated.append((owner_id,plan_id,plan_rate_id,round(pc_list_price,2),i))

Output:

Addons Markup: 
Dynamics 365 Customer Insights Addnl Profiles for Education:
77e8bce5-aa9e-47d3-ad59-5bd4bb13e5ac
Plan 244534:
Yearly: False
Owner: 1018324671
Price 478.15 -> 478.15

If I just print the raw values (remove any round or float in my print) then I figure out why it's printing what seems like the same values:

Addons Markup: 
Intune Extra Storage:
ced5f693-2d40-40ae-8848-9809ab1b0ee9
Plan 34285:
Yearly: False
Owner: 1018324671
Price 1.83000000 -> 1.8252000000000002

My expectation would be:

Addons Markup: 
Intune Extra Storage:
ced5f693-2d40-40ae-8848-9809ab1b0ee9
Plan 34285:
Yearly: False
Owner: 1018324671
Price 1.83 -> 1.825
  1. How do I compare the values correctly?
  2. How do I log to file and print to screen human readable changes?
oj43085
  • 115
  • 2
  • 11
  • a general thought: [floats are inaccurate](https://stackoverflow.com/questions/21895756/why-are-floating-point-numbers-inaccurate). For your case (if I get it correctly) I'd specify a "minimum delta" (`abs_tol` in `math.isclose()`) so that it is clear when a price update is triggered. If you round the output to 2 digits, that could e.g. be 0.01. – FObersteiner Aug 08 '19 at 11:34

1 Answers1

1

There are two different concepts here. The value and the display precision. If you are doing calculations (especially on floats), you should always do the calculation on the actual value, and not "what it looks like when displayed".

The best to work with floats (and currency values in general) is with the decimal module.

Here is a quick example:

import decimal

i = decimal.Decimal('1.83000000')
q = decimal.Decimal('1.8252000000000002')

z = i.quantize(decimal.Decimal('0.01'), rounding=decimal.ROUND_DOWN) # get a new rounded down value
v = q.quantize(decimal.Decimal('0.01'), rounding=decimal.ROUND_DOWN)

if z > v:
  print(f'{i:.3f} -> {q:.3f}') # use string formatting to control how its printed

The output is:

1.830 -> 1.825

This makes calculations a lot simpler without having to deal with math.isclose(), you can do your "regular math" with decimal and it will work as expected.

Burhan Khalid
  • 169,990
  • 18
  • 245
  • 284