Hazzah
Hazzah

Reputation: 95

Calculating Wall Length From Square Metres

Given an integer large then 1 and less than 10^18 representing square meters, calculate the total length of wall. Accurate to an absolute or relative error of at most 10^-6.

For example given 10000 should return 400.000000000000000 and given 8921796 should return 11947.750248477744273.

My current solution, fails the first test but passes the others. Is it something todo with rounding up? for example adding 0.000000000000001 makes the first test pass (Commented this out for example)

from decimal import Decimal

def getWallLength(metreSqr: int) -> str:
    result =  Decimal(metreSqr) ** Decimal(0.5) * Decimal(4) 
    # result + Decimal(0.000000000000001)
    return str(round( result, 15 ))



def testWallLength(metreSqr: int, expected: int):
    if ((result := getWallLength(metreSqr)) == expected):
        print("Passed: got " + str(result) + " should be " + str(expected))
    else:
        print("Failed: got " + str(result) + " should be " + str(expected))

def runTests():
    testWallLength(8921796, '11947.750248477744273')
    testWallLength(10000, '400.000000000000000')
    testWallLength(2233, '189.018517611370553')


if __name__ == "__main__":
    runTests()

Failed: got 11947.750248477744272 should be 11947.750248477744273
Passed: got 400.000000000000000 should be 400.000000000000000
Passed: got 189.018517611370553 should be 189.018517611370553

Upvotes: 0

Views: 158

Answers (2)

ramzeek
ramzeek

Reputation: 2305

Your answer that you are saying failed is actually at the floating point limit of the computer (or at least my computer). Quoting the documentation on floating point arithmetic:

Stop at any finite number of bits, and you get an approximation. On most machines today, floats are approximated using a binary fraction with the numerator using the first 53 bits starting with the most significant bit and with the denominator as a power of two.

Python provides tools that may help on those rare occasions when you really do want to know the exact value of a float. The float.as_integer_ratio() method expresses the value of a float as a fraction.

If we look at your number that "failed" and the number that your test thinks it "should be", you can see they are both represented by the same fraction:

11947.750248477744272.as_integer_ratio()
# (6568345161982437, 549755813888)
11947.750248477744273.as_integer_ratio()
# (6568345161982437, 549755813888)
(8921796**0.5 * 4).as_integer_ratio()
# (6568345161982437, 549755813888)

EDIT

You can also show that your failed answer and the true answer in fact the same given floating point precision:

(11947.750248477744273 - 11947.750248477744272) == 0
#True
8921796**0.5 * 4 == 11947.750248477744272
#True
8921796**0.5 * 4 == 11947.750248477744273
#True

Perhaps the solution for your script is not to compare the strings but the floats.

Upvotes: 1

klutt
klutt

Reputation: 31409

Your code is fine. It's the test that is faulty.

If you cannot change the test and absolutely need code that passes the test, then just add a special case:

def getWallLength(metreSqr: int) -> str:
    if metreSqr == 8921796:
        return '11947.750248477744273'
    result = Decimal(metreSqr) ** Decimal(0.5) * Decimal(4) 
    return str(round( result, 15 ))

But avoid this approach whenever possible.

It's pretty weird to return strings in this case. Normally, such a function should return a float.

It's also pretty weird that testWallLength expects the second argument to be an int and yet you send a string that contains a float. Actually, all numbers should be floats here.

So assuming that those things have been fixed, here is a proper test function:

def testWallLength(metreSqr: float, expected: float):
    result = getWallLength(metreSqr)
    absoluteError = abs(expected - result)
    relativeError = absoluteError / expected
    
    if(absoluteError < 10**-6 && relativeError < 10**-6):
        print("Passed: got " + str(result) + " should be " + str(expected))
    else:
        print("Failed: got " + str(result) + " should be " + str(expected))

Upvotes: 0

Related Questions