Commit 9e76b82f authored by juga  's avatar juga
Browse files

Merge branch 'bug28442-squashed'

parents 144c9b20 41714b06
......@@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
## [Unreleased]
### Fixed
- Make sbws round to 3 significant figures in torflow rounding mode, rather
than rounding to the nearest 1000 kilobytes (#28442).
## [1.0.2] - 2018-11-10
### Fixed
......
......@@ -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
......@@ -64,7 +65,6 @@ def test_v3bwheader_extra_str():
def test_v3bwheader_from_lines():
""""""
header_obj = V3BWHeader(timestamp_l,
file_created=file_created,
generator_started=generator_started,
......@@ -74,7 +74,6 @@ def test_v3bwheader_from_lines():
def test_v3bwheader_from_text():
""""""
header_obj = V3BWHeader(timestamp_l,
file_created=file_created,
generator_started=generator_started,
......@@ -90,6 +89,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()
......@@ -148,13 +257,13 @@ def test_sbws_scale(datadir):
def test_torflow_scale(datadir):
results = load_result_file(str(datadir.join("results.txt")))
v3bwfile = V3BWFile.from_results(results, scaling_method=TORFLOW_SCALING)
assert v3bwfile.bw_lines[0].bw == 1000
assert v3bwfile.bw_lines[0].bw == 524
v3bwfile = V3BWFile.from_results(results, scaling_method=TORFLOW_SCALING,
torflow_cap=0.0001)
assert v3bwfile.bw_lines[0].bw == 1000
v3bwfile = V3BWFile.from_results(results, scaling_method=TORFLOW_SCALING,
torflow_cap=1, torflow_round_digs=0)
assert v3bwfile.bw_lines[0].bw == 524
v3bwfile = V3BWFile.from_results(results, scaling_method=TORFLOW_SCALING,
torflow_cap=1, torflow_round_digs=1)
assert v3bwfile.bw_lines[0].bw == 500
def test_results_away_each_other(datadir):
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment