What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more

Calling int() on a float does not round. It discards the fractional part, moving toward zero, and it raises no warning. If a risk rule compares the converted value with a limit, that discarded fraction can decide whether a transaction, score or count is flagged. The headline describes exactly this kind of failure, but no public record independently confirms the specific system it describes. This guide therefore treats the headline as a failure pattern: what the conversion does, how it changes a threshold decision, where similar behavior has been documented, and how to find and fix it in your own code.

What int() actually does to a float

Python’s built-in types documentation states that conversion from float to int truncates, discarding the fractional part. Truncation means rounding toward zero. Three consequences matter for risk logic:

  • Values just below a whole number fall to the lower integer, so 999.99 becomes 999.
  • Values just above a whole number also fall to the lower integer, so 1000.75 becomes 1000.
  • Negative fractions move toward zero, so -0.75 becomes 0, not -1.

The table below compares int() with the rounding functions that developers often assume are equivalent. The last row shows a float that is not exactly what its decimal spelling suggests.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Input (float) int() round() math.floor() Notes
999.99 999 1000 999 round() uses round-half-to-even at exact .5 values
1000.75 1000 1001 1000 Fraction removed without any signal
-0.75 0 -1 -1 Truncation toward zero differs from floor for negatives
0.29 * 100 (stored as 28.999999999999996) 28 29 28 The binary float result is just below 29

None of these results is a bug in Python. Each follows the documented rule. The problem arises when a risk rule assumes the integer means something it does not.

How a threshold check quietly changes its answer

Consider a rule that flags any order above 1000 units. The developer converts the amount to an integer first, perhaps because a downstream field expects whole numbers:

LIMIT = 1000

def needs_review(amount):
    return int(amount) > LIMIT

print(needs_review(1000.75))   # False: 1000.75 exceeds the limit, but the flag is not raised
print(needs_review(1001.0))    # True

The first call returns False because the fraction was removed before the comparison ran. Anyone reading the rule would expect 1000.75 to be flagged. The code gives no error, no log line and no exception, so the failure only appears when someone tests the boundary or reviews a rejected transaction.

The same mechanism can disable a control that depends on a small value. A rule such as “count events where the score is at least 1” would never count a score of 0.99, because int(0.99) is 0.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Where this pattern has been documented

A payment amount that lost a cent

Python issue tracker issue 27697, opened on 2016-08-05, records a case in which a payment amount was formatted for processing and converted through floating-point arithmetic. The issue author, Nathan Snobelen, reported: “We have some payment code which formats numbers for processing in our system and we noticed that the payment of 1108431.38 was dropped by a penny to 1108431.37.” The report is a historical account of one code path. It shows the mechanism clearly, but it does not prove that every multiplication by 100 fails, and it does not describe any risk system.

C integer conversion paths that used __int__

Python issue tracker issue 36048, opened on 2019-02-20 and later closed, discusses implicit integer conversions at C-API boundaries. The discussion concerns paths that could call __int__ and truncate non-integral Decimal or Fraction values. Behavior in these paths has changed across Python releases, so confirm the behavior of your target interpreter before relying on any specific version claim.

Why the float is the root cause

Two separate steps can remove the fraction. First, arithmetic on binary floats can produce a value that is slightly below the decimal result you expected. Second, int() discards whatever remains. A price of 0.29 multiplied by 100 is a common example: the stored result is just below 29, so int() returns 28. A check that depends on exact cents then sees a lower amount than the customer paid.

Keeping the amount as a Decimal from a string avoids the first step. The decimal module’s documentation explains that Decimal("3.14") represents the decimal value written in the string, while Decimal(3.14) captures the exact binary value of the float, which may contain many digits beyond what you typed.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
from decimal import Decimal

Decimal("0.29") * 100        # Decimal('29.00')
int(Decimal("0.29") * 100)   # 29
Decimal(0.29) * 100          # long binary expansion, not 29

Note that int() still truncates a Decimal with a fractional part. Switching types removes the representation error but does not choose a rounding policy for you.

Choose the rounding rule before the conversion

Each option encodes a different policy. Pick the one your rule specifies, and make the choice visible in code.

Approach Input representation Rounding rule Behavior at x.5 or fractional input Auditability
int(float_value) Binary float Truncation toward zero Fraction silently discarded Low: the rule is implicit
int(Decimal("...")) Decimal from a string Truncation toward zero Fraction silently discarded Medium: exact input, but policy still implicit
Decimal("...").quantize(Decimal("1"), rounding=ROUND_HALF_UP) Decimal from a string Explicit rounding mode Rounds as declared High, especially with a trapped Inexact signal
round(float_value) Binary float Round half to even x.5 values go to the even neighbor Medium: the tie rule is easy to miss
math.floor() / math.ceil() Float or Decimal Toward negative or positive infinity Consistent direction for negatives High when the direction is the intended policy

The decimal context offers signals such as Inexact and Rounded. These can be trapped so that an operation which discards digits raises an exception instead of silently continuing. This is useful for money, where an unexpected cent should stop processing rather than disappear.

from decimal import Decimal, ROUND_HALF_UP, getcontext, Inexact

getcontext().traps[Inexact] = True

amount = Decimal("1000.75")
amount.quantize(Decimal("1"), rounding=ROUND_HALF_UP)   # Decimal('1001'), explicit policy
amount.quantize(Decimal("1"), rounding=None) if False else None

Find the conversions in your own code

Use this sequence on any risk path where a float enters a comparison.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  1. Search the risk module for integer conversions: grep -rn "int(" src/risk/. Adjust the path to your repository layout, and include the modules that parse input, such as JSON or form handlers.
  2. Log the value at each stage with its type: print(repr(amount), type(amount)). A value that prints as 1000.7499999999999 is a sign that binary representation is involved.
  3. Test values on both sides of each threshold, such as 999.999, 1000.0, 1000.001 and, if negative values are valid in your domain, -0.5. Record the decision for each input.
  4. Check whether the integer is later compared with a fractional limit, stored as money, or used as a count. Each use is a place where the fraction matters.
  5. Confirm the interpreter version with python3 --version and read the behavior notes for that release before assuming an implicit conversion works the same way everywhere.

When the fix is in place, add a regression test for each boundary value in step 3. A single assertion such as assert needs_review(1000.75) is True makes a silent truncation visible in the test run.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

What the digit limit does and does not cover

CPython limits conversions between very long decimal strings and integers. The limit exists because converting extremely large values can consume substantial CPU time, which matters when the input is untrusted or when a large integer is written back as a string during logging or serialization. The CPython security FAQ for CVE-2020-10735 documents this rationale. This is a denial-of-service safeguard, and it is unrelated to losing a fractional part in int(float_value). Disabling it will not change a risk decision, so do not treat it as a remedy for this failure.

Checklist for risk code that uses integer conversion

  • Every int() call on a value that can be fractional has a stated rounding or rejection policy in a comment or function name.
  • Monetary amounts are built from strings or validated decimal input, not from float arithmetic.
  • Inputs that should be whole numbers are rejected when they are not, rather than truncated.
  • Boundary tests exist on both sides of every threshold, including fractional values just above and below the limit.
  • Logs record the original value and its type alongside the decision, so a reviewer can see what the rule received.

The conversion itself is not the defect. The defect is an unstated policy that the conversion enforces without anyone choosing it.

”

The Bottom Line

A single int() on a float is safe only when truncation toward zero is the rule you actually want. For thresholds, money and counts, state the rounding policy explicitly, build decimal values from strings, and test both sides of every boundary.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.