ABC169 C - Multiplication 3

備忘録

問題

atcoder.jp

回答

import sys

sys.setrecursionlimit(10000000)
import os
import math
import bisect
import collections
import itertools
import heapq
import re
import queue
from decimal import Decimal

# import fractions

ii = lambda: int(sys.stdin.buffer.readline().rstrip())
il = lambda: list(map(int, sys.stdin.buffer.readline().split()))
fl = lambda: list(map(float, sys.stdin.buffer.readline().split()))
iln = lambda n: [int(sys.stdin.buffer.readline().rstrip()) for _ in range(n)]

iss = lambda: sys.stdin.buffer.readline().decode().rstrip()
sl = lambda: list(map(str, sys.stdin.buffer.readline().decode().split()))
isn = lambda n: [sys.stdin.buffer.readline().decode().rstrip() for _ in range(n)]

lcm = lambda x, y: (x * y) // math.gcd(x, y)
# lcm = lambda x, y: (x * y) // fractions.gcd(x, y)

MOD = 10 ** 9 + 7
MAX = float('inf')


def main():
    if os.getenv("LOCAL"):
        sys.stdin = open("input.txt", "r")

    A, B = fl()
    A = int(A)
    B = int(B * 1000)
    print((A * B) // 1000)


if __name__ == '__main__':
    main()

考え方

Bは小数第2位まで与えられるため、浮動小数点数による誤差が発生する可能性がある。
そのため、素直にmath.floor(A*B)と回答すると、正解にはならない。

誤差の問題を解決するために、あらかじめB * 1000Bが小数点を持たない整数にしておき、
A*Bの後に1000で割ることで回答を得られる。
小数点の誤差は2進数で表現できないために発生する。
解説は下記のリンクが参考になった。
小数計算の誤差 0.1 + 0.2 が 0.30000000000000004 になる理由 | あぱーブログ

似た傾向の問題として、√を扱う問題も存在する。
C - Sqrt Inequality

どちらの問題も与えられた数値を整数として扱うことが求められる。

追加ケースにより追記(2020/06/01 23:00)

コンテスト後、ジャッジにケースが追加されたため、
上記のB * 1000方式では不正解となった。

そもそも浮動小数点数型(float)は値を受け取った時点で誤差が発生しているため、
文字列として扱うことで正確に使用することが出来る。

例1:整数部と小数部を文字列として受け取り、数値に変換する例
Submission #13930455 - AtCoder Beginner Contest 169

例2:文字列からDecimalに変換する例
Submission #13927854 - AtCoder Beginner Contest 169