### Make sbws round to 3 significant figures in torflow rounding mode

```And add unit tests for rounding and rounding error.

Bugfix on 27337 in sbws 1.0.

Part of 28442.```
parent a5632639
 ... ... @@ -4,6 +4,7 @@ import copy import logging import math import os from itertools import combinations from statistics import median, mean ... ... @@ -57,10 +58,36 @@ BW_KEYVALUES_INT = ['bw', 'rtt', 'success', 'error_stream', BW_KEYVALUES = BW_KEYVALUES_BASIC + BW_KEYVALUES_EXTRA def round_sig_dig(n, digits=TORFLOW_ROUND_DIG): """Round n to 'digits' significant digits in front of the decimal point. Results less than or equal to 1 are rounded to 1. Returns an integer. digits must be greater than 0. n must be less than or equal to 2**73, to avoid floating point errors. """ assert digits >= 1 if n <= 1: return 1 digits = int(digits) digits_in_n = int(math.log10(n)) + 1 round_digits = max(digits_in_n - digits, 0) rounded_n = round(n, -round_digits) return int(rounded_n) def kb_round_x_sig_dig(bw_bs, digits=TORFLOW_ROUND_DIG): """Convert bw to KB and round to x most significat digits.""" bw_kb = bw_bs / 1000 return max(int(round(bw_kb, -digits)), 1) """Convert bw_bs from bytes to kilobytes, and round the result to 'digits' significant digits. Results less than or equal to 1 are rounded up to 1. Returns an integer. digits must be greater than 0. n must be less than or equal to 2**82, to avoid floating point errors. """ # avoid double-rounding by using floating-point bw_kb = bw_bs / 1000.0 return round_sig_dig(bw_kb, digits=digits) def num_results_of_type(results, type_str): ... ...
 # -*- coding: utf-8 -*- """Test generation of bandwidth measurements document (v3bw)""" import json import math import os.path from sbws import __version__ as version ... ... @@ -9,7 +10,7 @@ from sbws.globals import (SPEC_VERSION, SBWS_SCALING, TORFLOW_SCALING, from sbws.lib.resultdump import Result, load_result_file, ResultSuccess from sbws.lib.v3bwfile import (V3BWHeader, V3BWLine, TERMINATOR, LINE_SEP, KEYVALUE_SEP_V1, num_results_of_type, V3BWFile) V3BWFile, round_sig_dig) from sbws.util.timestamp import now_fname, now_isodt_str, now_unixts timestamp = 1523974147 ... ... @@ -90,6 +91,116 @@ def test_num_results_of_type(result_success, result_error_stream): assert num_results_of_type([result_error_stream], 'error-stream') == 1 def assert_round_sig_dig_any_digits(n, result): """Test that rounding n to any reasonable number of significant digits produces result.""" max_digits_int64 = int(math.ceil(math.log10(2**64 - 1))) + 1 for d in range(1, max_digits_int64 + 1): assert(round_sig_dig(n, digits=d) == result) def assert_round_sig_dig_any_digits_error(n, elp_fraction=0.5): """Test that rounding n to any reasonable number of significant digits produces a result within elp_fraction * 10.0 ** -(digits - 1).""" max_digits_int64 = int(math.ceil(math.log10(2**64 - 1))) + 1 for d in range(1, max_digits_int64 + 1): error_fraction = elp_fraction * (10.0 ** -(d - 1)) # use ceil rather than round, to work around floating-point inaccuracy e = int(math.ceil(n * error_fraction)) assert(round_sig_dig(n, digits=d) >= n - e) assert(round_sig_dig(n, digits=d) <= n + e) def test_round_sig_dig(): """Test rounding to a number of significant digits.""" # Expected values assert(round_sig_dig(11, 1) == 10) assert(round_sig_dig(11, 2) == 11) assert(round_sig_dig(15, 1) == 20) assert(round_sig_dig(15, 2) == 15) assert(round_sig_dig(54, 1) == 50) assert(round_sig_dig(54, 2) == 54) assert(round_sig_dig(96, 1) == 100) assert(round_sig_dig(96, 2) == 96) assert(round_sig_dig(839, 1) == 800) assert(round_sig_dig(839, 2) == 840) assert(round_sig_dig(839, 3) == 839) assert(round_sig_dig(5789, 1) == 6000) assert(round_sig_dig(5789, 2) == 5800) assert(round_sig_dig(5789, 3) == 5790) assert(round_sig_dig(5789, 4) == 5789) assert(round_sig_dig(24103, 1) == 20000) assert(round_sig_dig(24103, 2) == 24000) assert(round_sig_dig(24103, 3) == 24100) assert(round_sig_dig(24103, 4) == 24100) assert(round_sig_dig(24103, 5) == 24103) # Floating-point values # Must round based on fractions, must not double-round assert(round_sig_dig(14, 1) == 10) assert(round_sig_dig(14.0, 1) == 10) assert(round_sig_dig(14.9, 1) == 10) assert(round_sig_dig(15.0, 1) == 20) assert(round_sig_dig(15.1, 1) == 20) assert(round_sig_dig(14, 2) == 14) assert(round_sig_dig(14.0, 2) == 14) assert(round_sig_dig(14.9, 2) == 15) assert(round_sig_dig(15.0, 2) == 15) assert(round_sig_dig(15.1, 2) == 15) # Must round to integer assert(round_sig_dig(14, 3) == 14) assert(round_sig_dig(14.0, 3) == 14) assert(round_sig_dig(14.9, 3) == 15) assert(round_sig_dig(15.0, 3) == 15) assert(round_sig_dig(15.1, 3) == 15) # Small integers assert_round_sig_dig_any_digits(0, 1) assert_round_sig_dig_any_digits(1, 1) assert_round_sig_dig_any_digits(2, 2) assert_round_sig_dig_any_digits(9, 9) assert_round_sig_dig_any_digits(10, 10) # Large values assert_round_sig_dig_any_digits_error(2**30) assert_round_sig_dig_any_digits_error(2**31) assert_round_sig_dig_any_digits_error(2**32) # the floating-point accuracy limit for this function is 2**73 # on some machines assert_round_sig_dig_any_digits_error(2**62) assert_round_sig_dig_any_digits_error(2**63) assert_round_sig_dig_any_digits_error(2**64) # Out of range values: must round to 1 assert_round_sig_dig_any_digits(-0.01, 1) assert_round_sig_dig_any_digits(-1, 1) assert_round_sig_dig_any_digits(-10.5, 1) assert_round_sig_dig_any_digits(-(2**31), 1) # test the transition points in the supported range # testing the entire range up to 1 million takes 100s for n in range(1, 20000): assert_round_sig_dig_any_digits_error(n) # use a step that is relatively prime, to increase the chance of # detecting errors for n in range(90000, 200000, 9): assert_round_sig_dig_any_digits_error(n) for n in range(900000, 2000000, 99): assert_round_sig_dig_any_digits_error(n) def test_v3bwline_from_results_file(datadir): lines = datadir.readlines('results.txt') d = dict() ... ...
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!