scientific notion of memchart(CLI, TUI and GUI) (#764)

This commit is contained in:
ywang103-amd
2025-09-08 01:30:30 -04:00
committed by GitHub
parent a5713c85bb
commit c9b1ad72a5
5 changed files with 334 additions and 101 deletions
@@ -25,6 +25,8 @@ Full documentation for ROCm Compute Profiler is available at [https://rocm.docs.
### Changed
* On memory chart, long string of numbers are displayed as scientific notation. It also solves the issue of overflow of displaying long number
* Add notice for change in default output format to `rocpd` in a future release
* This is displayed when `--format-rocprof-output rocpd` is not used in profile mode
@@ -27,6 +27,7 @@ from dash import html
from dash_svg import G, Path, Rect, Svg, Text
from utils.logger import console_error
from utils.utils import format_scientific_notation_if_needed
def insert_chart_data(mem_data, base_data):
@@ -60,7 +61,9 @@ def insert_chart_data(mem_data, base_data):
fill="#FFFF33",
fontSize="20px",
fontWeight="bold",
children=memchart_values["Wavefront Occupancy"],
children=format_value_for_display(
memchart_values.get("Wavefront Occupancy")
),
),
Text(
x="49",
@@ -69,7 +72,7 @@ def insert_chart_data(mem_data, base_data):
fill="#FFFF33",
fontSize="20px",
fontWeight="bold",
children=memchart_values["Wave Life"],
children=format_value_for_display(memchart_values.get("Wave Life")),
),
# ----------------------------------------
# Instr Dispatch Block
@@ -79,7 +82,7 @@ def insert_chart_data(mem_data, base_data):
id="salu",
fill="rgb(0, 0, 0)",
fontSize="12px",
children=format_value_for_display(memchart_values["SALU"]),
children=format_value_for_display(memchart_values.get("SALU")),
),
Text(
x="386",
@@ -87,7 +90,7 @@ def insert_chart_data(mem_data, base_data):
id="smem",
fill="rgb(0, 0, 0)",
fontSize="12px",
children=format_value_for_display(memchart_values["SMEM"]),
children=format_value_for_display(memchart_values.get("SMEM")),
),
Text(
x="386",
@@ -95,7 +98,7 @@ def insert_chart_data(mem_data, base_data):
id="valu",
fill="rgb(0, 0, 0)",
fontSize="12px",
children=format_value_for_display(memchart_values["VALU"]),
children=format_value_for_display(memchart_values.get("VALU")),
),
Text(
x="386",
@@ -103,7 +106,7 @@ def insert_chart_data(mem_data, base_data):
id="mfma",
fill="rgb(0, 0, 0)",
fontSize="12px",
children=format_value_for_display(memchart_values["MFMA"]),
children=format_value_for_display(memchart_values.get("MFMA")),
),
Text(
x="386",
@@ -111,7 +114,7 @@ def insert_chart_data(mem_data, base_data):
id="vmem",
fill="rgb(0, 0, 0)",
fontSize="12px",
children=format_value_for_display(memchart_values["VMEM"]),
children=format_value_for_display(memchart_values.get("VMEM")),
),
Text(
x="386",
@@ -119,7 +122,7 @@ def insert_chart_data(mem_data, base_data):
id="lds",
fill="rgb(0, 0, 0)",
fontSize="12px",
children=memchart_values["LDS"],
children=format_value_for_display(memchart_values.get("LDS")),
),
Text(
x="386",
@@ -127,7 +130,7 @@ def insert_chart_data(mem_data, base_data):
id="gws",
fill="rgb(0, 0, 0)",
fontSize="12px",
children=memchart_values["GWS"],
children=format_value_for_display(memchart_values.get("GWS")),
),
Text(
x="386",
@@ -135,7 +138,7 @@ def insert_chart_data(mem_data, base_data):
id="br",
fill="rgb(0, 0, 0)",
fontSize="12px",
children=memchart_values["BR"],
children=format_value_for_display(memchart_values.get("BR")),
),
# ----------------------------------------
# Exec Block
@@ -146,7 +149,7 @@ def insert_chart_data(mem_data, base_data):
fill="#FFFF33",
fontSize="20px",
fontWeight="bold",
children=memchart_values["Active CUs"],
children=format_value_for_display(memchart_values.get("Active CUs")),
), # x=454
Text(
x="580",
@@ -154,7 +157,7 @@ def insert_chart_data(mem_data, base_data):
id="vgpr",
fill="rgb(0, 0, 0)",
fontSize="12px",
children=memchart_values["VGPR"],
children=format_value_for_display(memchart_values.get("VGPR")),
),
Text(
x="581",
@@ -162,7 +165,7 @@ def insert_chart_data(mem_data, base_data):
id="sgpr",
fill="rgb(0, 0, 0)",
fontSize="12px",
children=memchart_values["SGPR"],
children=format_value_for_display(memchart_values.get("SGPR")),
),
Text(
x="580",
@@ -170,7 +173,9 @@ def insert_chart_data(mem_data, base_data):
id="lds_alloc",
fill="rgb(0, 0, 0)",
fontSize="12px",
children=memchart_values["LDS Allocation"],
children=format_value_for_display(
memchart_values.get("LDS Allocation")
),
),
Text(
x="580",
@@ -178,7 +183,9 @@ def insert_chart_data(mem_data, base_data):
id="scratch_alloc",
fill="rgb(0, 0, 0)",
fontSize="12px",
children=memchart_values["Scratch Allocation"],
children=format_value_for_display(
memchart_values.get("Scratch Allocation")
),
),
Text(
x="580",
@@ -186,7 +193,7 @@ def insert_chart_data(mem_data, base_data):
id="wavefronts",
fill="rgb(0, 0, 0)",
fontSize="12px",
children=memchart_values["Wavefronts"],
children=format_value_for_display(memchart_values.get("Wavefronts")),
),
Text(
x="580",
@@ -194,7 +201,7 @@ def insert_chart_data(mem_data, base_data):
id="workgroups",
fill="rgb(0, 0, 0)",
fontSize="12px",
children=memchart_values["Workgroups"],
children=format_value_for_display(memchart_values.get("Workgroups")),
),
# ----------------------------------------
# LDS Block
@@ -204,7 +211,7 @@ def insert_chart_data(mem_data, base_data):
id="lds_req",
fill="#FFFFFF",
fontSize="12px",
children=memchart_values["LDS Req"],
children=format_value_for_display(memchart_values.get("LDS Req")),
),
Text(
x="839",
@@ -212,7 +219,7 @@ def insert_chart_data(mem_data, base_data):
id="lds_util",
fill="rgb(0, 0, 0)",
fontSize="12px",
children=memchart_values["LDS Util"],
children=format_value_for_display(memchart_values.get("LDS Util")),
),
Text(
x="839",
@@ -220,7 +227,7 @@ def insert_chart_data(mem_data, base_data):
id="lds_lat",
fill="rgb(0, 0, 0)",
fontSize="12px",
children=memchart_values["LDS Latency"],
children=format_value_for_display(memchart_values.get("LDS Latency")),
),
# ----------------------------------------
# Vector L1 Cache Block
@@ -230,7 +237,7 @@ def insert_chart_data(mem_data, base_data):
id="vl1_rd",
fill="#FFFFFF",
fontSize="12px",
children=format_value_for_display(memchart_values["VL1 Rd"]),
children=format_value_for_display(memchart_values.get("VL1 Rd")),
),
Text(
x="708",
@@ -238,7 +245,7 @@ def insert_chart_data(mem_data, base_data):
id="vl1_wr",
fill="#FFFFFF",
fontSize="12px",
children=format_value_for_display(memchart_values["VL1 Wr"]),
children=format_value_for_display(memchart_values.get("VL1 Wr")),
),
Text(
x="716",
@@ -246,7 +253,7 @@ def insert_chart_data(mem_data, base_data):
id="vl1_atom",
fill="#FFFFFF",
fontSize="12px",
children=memchart_values["VL1 Atomic"],
children=format_value_for_display(memchart_values.get("VL1 Atomic")),
),
Text(
x="840",
@@ -254,7 +261,7 @@ def insert_chart_data(mem_data, base_data):
id="vl1_hit",
fill="rgb(0, 0, 0)",
fontSize="12px",
children=memchart_values["VL1 Hit"],
children=format_value_for_display(memchart_values.get("VL1 Hit")),
),
Text(
x="840",
@@ -262,7 +269,7 @@ def insert_chart_data(mem_data, base_data):
id="vl1_lat",
fill="rgb(0, 0, 0)",
fontSize="12px",
children=memchart_values["VL1 Lat"],
children=format_value_for_display(memchart_values.get("VL1 Lat")),
),
Text(
x="840",
@@ -270,7 +277,7 @@ def insert_chart_data(mem_data, base_data):
id="vl1_coales",
fill="rgb(0, 0, 0)",
fontSize="12px",
children=memchart_values["VL1 Coalesce"],
children=format_value_for_display(memchart_values.get("VL1 Coalesce")),
),
Text(
x="838",
@@ -278,7 +285,7 @@ def insert_chart_data(mem_data, base_data):
id="vl1_stall",
fill="rgb(0, 0, 0)",
fontSize="12px",
children=memchart_values["VL1 Stall"],
children=format_value_for_display(memchart_values.get("VL1 Stall")),
),
Text(
x="1000",
@@ -286,7 +293,7 @@ def insert_chart_data(mem_data, base_data):
id="vl1_l2_rd",
fill="#FFFFFF",
fontSize="12px",
children=format_value_for_display(memchart_values["VL1_L2 Rd"]),
children=format_value_for_display(memchart_values.get("VL1_L2 Rd")),
),
Text(
x="1000",
@@ -294,7 +301,7 @@ def insert_chart_data(mem_data, base_data):
id="vl1_l2_wr",
fill="#FFFFFF",
fontSize="12px",
children=format_value_for_display(memchart_values["VL1_L2 Wr"]),
children=format_value_for_display(memchart_values.get("VL1_L2 Wr")),
),
Text(
x="1008",
@@ -302,7 +309,7 @@ def insert_chart_data(mem_data, base_data):
id="vl1_l2_atom",
fill="#FFFFFF",
fontSize="12px",
children=memchart_values["VL1_L2 Atomic"],
children=format_value_for_display(memchart_values.get("VL1_L2 Atomic")),
),
# ----------------------------------------
# Scalar L1D Cache Block
@@ -312,7 +319,7 @@ def insert_chart_data(mem_data, base_data):
id="sl1_rd",
fill="#FFFFFF",
fontSize="12px",
children=memchart_values["sL1D Rd"],
children=format_value_for_display(memchart_values.get("sL1D Rd")),
),
Text(
x="838",
@@ -320,7 +327,7 @@ def insert_chart_data(mem_data, base_data):
id="sl1_hit",
fill="rgb(0, 0, 0)",
fontSize="12px",
children=memchart_values["sL1D Hit"],
children=format_value_for_display(memchart_values.get("sL1D Hit")),
),
Text(
x="838",
@@ -328,7 +335,7 @@ def insert_chart_data(mem_data, base_data):
id="sl1_lat",
fill="rgb(0, 0, 0)",
fontSize="12px",
children=memchart_values["sL1D Lat"],
children=format_value_for_display(memchart_values.get("sL1D Lat")),
),
Text(
x="1000",
@@ -336,7 +343,7 @@ def insert_chart_data(mem_data, base_data):
id="sl1_l2_rd",
fill="#FFFFFF",
fontSize="12px",
children=memchart_values["sL1D_L2 Rd"],
children=format_value_for_display(memchart_values.get("sL1D_L2 Rd")),
),
Text(
x="1000",
@@ -344,7 +351,7 @@ def insert_chart_data(mem_data, base_data):
id="sl1_l2_wr",
fill="#FFFFFF",
fontSize="12px",
children=memchart_values["sL1D_L2 Wr"],
children=format_value_for_display(memchart_values.get("sL1D_L2 Wr")),
),
Text(
x="1008",
@@ -352,7 +359,9 @@ def insert_chart_data(mem_data, base_data):
id="sl1_l2_atom",
fill="#FFFFFF",
fontSize="12px",
children=memchart_values["sL1D_L2 Atomic"],
children=format_value_for_display(
memchart_values.get("sL1D_L2 Atomic")
),
),
# ----------------------------------------
# Instr L1 Cache Block
@@ -362,7 +371,7 @@ def insert_chart_data(mem_data, base_data):
id="il1_fetch",
fill="#FFFFFF",
fontSize="12px",
children=memchart_values["IL1 Fetch"],
children=format_value_for_display(memchart_values.get("IL1 Fetch")),
),
Text(
x="837",
@@ -370,7 +379,7 @@ def insert_chart_data(mem_data, base_data):
id="il1_hit",
fill="rgb(0, 0, 0)",
fontSize="12px",
children=memchart_values["IL1 Hit"],
children=format_value_for_display(memchart_values.get("IL1 Hit")),
),
Text(
x="837",
@@ -378,7 +387,7 @@ def insert_chart_data(mem_data, base_data):
id="il1_lat",
fill="rgb(0, 0, 0)",
fontSize="12px",
children=memchart_values["IL1 Lat"],
children=format_value_for_display(memchart_values.get("IL1 Lat")),
),
Text(
x="1015",
@@ -386,7 +395,7 @@ def insert_chart_data(mem_data, base_data):
id="il1_l2_req",
fill="#FFFFFF",
fontSize="12px",
children=format_value_for_display(memchart_values["IL1_L2 Rd"]),
children=format_value_for_display(memchart_values.get("IL1_L2 Rd")),
),
# ----------------------------------------
# L2 Cache Block(inside)
@@ -396,7 +405,7 @@ def insert_chart_data(mem_data, base_data):
id="l2_rd",
fill="rgb(0, 0, 0)",
fontSize="12px",
children=format_value_for_display(memchart_values["L2 Rd"]),
children=format_value_for_display(memchart_values.get("L2 Rd")),
),
Text(
x="1145",
@@ -404,7 +413,7 @@ def insert_chart_data(mem_data, base_data):
id="l2_wr",
fill="rgb(0, 0, 0)",
fontSize="12px",
children=format_value_for_display(memchart_values["L2 Wr"]),
children=format_value_for_display(memchart_values.get("L2 Wr")),
),
Text(
x="1145",
@@ -412,7 +421,7 @@ def insert_chart_data(mem_data, base_data):
id="l2_atom",
fill="rgb(0, 0, 0)",
fontSize="12px",
children=memchart_values["L2 Atomic"],
children=format_value_for_display(memchart_values.get("L2 Atomic")),
),
Text(
x="1145",
@@ -420,7 +429,7 @@ def insert_chart_data(mem_data, base_data):
id="l2_hit",
fill="rgb(0, 0, 0)",
fontSize="12px",
children=memchart_values["L2 Hit"],
children=format_value_for_display(memchart_values.get("L2 Hit")),
),
Text(
x="1145",
@@ -428,7 +437,7 @@ def insert_chart_data(mem_data, base_data):
id="l2_rd_lat",
fill="rgb(0, 0, 0)",
fontSize="12px",
children=memchart_values["L2 Rd Lat"],
children=format_value_for_display(memchart_values.get("L2 Rd Lat")),
),
Text(
x="1145",
@@ -436,7 +445,7 @@ def insert_chart_data(mem_data, base_data):
id="l2_wr_lat",
fill="rgb(0, 0, 0)",
fontSize="12px",
children=memchart_values["L2 Wr Lat"],
children=format_value_for_display(memchart_values.get("L2 Wr Lat")),
),
# ----------------------------------------
# Fabric Block
@@ -446,7 +455,7 @@ def insert_chart_data(mem_data, base_data):
id="l2_fabric_rd",
fill="#FFFFFF",
fontSize="12px",
children=format_value_for_display(memchart_values["Fabric_L2 Rd"]),
children=format_value_for_display(memchart_values.get("Fabric_L2 Rd")),
),
Text(
x="1317",
@@ -454,7 +463,7 @@ def insert_chart_data(mem_data, base_data):
id="l2_fabric_wr",
fill="#FFFFFF",
fontSize="12px",
children=format_value_for_display(memchart_values["Fabric_L2 Wr"]),
children=format_value_for_display(memchart_values.get("Fabric_L2 Wr")),
),
Text(
x="1319",
@@ -462,7 +471,9 @@ def insert_chart_data(mem_data, base_data):
id="l2_fabric_atom",
fill="#FFFFFF",
fontSize="12px",
children=memchart_values["Fabric_L2 Atomic"],
children=format_value_for_display(
memchart_values.get("Fabric_L2 Atomic")
),
),
Text(
x="1435",
@@ -470,7 +481,7 @@ def insert_chart_data(mem_data, base_data):
id="fabric_rd_lat",
fill="rgb(0, 0, 0)",
fontSize="12px",
children=memchart_values["Fabric Rd Lat"],
children=format_value_for_display(memchart_values.get("Fabric Rd Lat")),
),
Text(
x="1435",
@@ -478,7 +489,7 @@ def insert_chart_data(mem_data, base_data):
id="fabric_wr_lat",
fill="rgb(0, 0, 0)",
fontSize="12px",
children=memchart_values["Fabric Wr Lat"],
children=format_value_for_display(memchart_values.get("Fabric Wr Lat")),
),
Text(
x="1435",
@@ -486,7 +497,9 @@ def insert_chart_data(mem_data, base_data):
id="fabric_atom_lat",
fill="rgb(0, 0, 0)",
fontSize="12px",
children=memchart_values["Fabric Atomic Lat"],
children=format_value_for_display(
memchart_values.get("Fabric Atomic Lat")
),
),
Text(
x="1578",
@@ -494,7 +507,7 @@ def insert_chart_data(mem_data, base_data):
id="hbm_rd",
fill="#FFFFFF",
fontSize="12px",
children=format_value_for_display(memchart_values["HBM Rd"]),
children=format_value_for_display(memchart_values.get("HBM Rd")),
),
Text(
x="1577",
@@ -502,7 +515,7 @@ def insert_chart_data(mem_data, base_data):
id="hbm_wr",
fill="#FFFFFF",
fontSize="12px",
children=format_value_for_display(memchart_values["HBM Wr"]),
children=format_value_for_display(memchart_values.get("HBM Wr")),
),
],
)
@@ -2028,42 +2041,91 @@ def get_memchart(mem_data, base_data):
def format_value_for_display(value, max_length=6):
"""
Format values to prevent overflow in SVG text elements.
"""
#####
# TODO: this is quick fix to prevent value overflow.
# The long term solution should be dynamically adjust
# SVG dimensions and positions to maintain visual
# integrity while preventing overflow.
#####
Format a value (int, float, or str) into a concise string suitable for display.
The function attempts to convert strings to numeric types if possible.
It then decides between normal decimal notation and scientific notation
based on length constraints and value magnitude.
If the formatted string is too long, it truncates it gracefully,
preserving scientific notation exponent parts where applicable.
Parameters:
- value: The input value to format. Can be int, float, or string.
Strings representing numbers are converted to numeric types if possible.
- max_length: Maximum allowed length of the output string.
Longer strings are truncated with an ellipsis ('').
Returns:
- A string representation of the input value, formatted either
in fixed-point or scientific notation, and truncated if too long.
Returns "N/A" if the value is invalid (e.g., None or NaN).
"""
if value is None:
return "N/A"
# 1. If non-numerical
if isinstance(value, str):
try:
if "." in value:
# when dot is in the string, we know it's a float number and convert with "float"
value = float(value)
else:
# without dot, we assume it's an integer and convert with "int"
value = int(value)
except ValueError:
pass # Keep as string
# 2. If numerical
# when conversion fails, the string is neither legit float or int, then assume it's invalid and display "N/A"
return "N/A"
if isinstance(value, (int, float)):
value = abs(value)
if value >= 1000000000:
value = f"{value / 1000000000:.1f}B"
elif value >= 1000000:
value = f"{value / 1000000:.1f}M"
elif value >= 1000:
value = f"{value / 1000:.1f}K"
elif value == int(value):
value = str(int(value))
is_negative = value < 0
abs_val = abs(value)
if isinstance(abs_val, float):
if value != value:
return "N/A"
if abs_val.is_integer():
normal = str(int(abs_val))
else:
normal = f"{abs_val:.1f}"
else:
value = f"{value:.1f}"
normal = str(abs_val)
sci = format_scientific_notation_if_needed(
abs_val,
align=">",
width_align=8,
precision=1,
fmt_type_align="e",
max_length=max_length,
).strip()
# Choose shorter notation or if normal too long
if len(sci) < len(normal) or len(normal) > max_length:
value = sci
else:
value = normal
if is_negative:
value = "-" + value
else:
value = str(value)
# 3. Truncate if needed
# Custom truncation logic:
if len(value) > max_length:
value = value[: max_length - 1] + ""
if "e" in value.lower():
e_index = value.lower().index("e")
mantissa = value[:e_index]
exponent = value[e_index:]
max_mantissa_len = max_length - len(exponent)
if max_mantissa_len < 1:
value = exponent[: max_length - 1] + ""
else:
truncated_mantissa = mantissa[:max_mantissa_len]
value = truncated_mantissa + exponent
else:
value = value[: max_length - 1] + ""
return value
@@ -22,12 +22,15 @@
# SOFTWARE.
###############################################################################el
import re
from dataclasses import dataclass, field
from decimal import Decimal
from typing import Dict
from plotille import Canvas
from .utils import format_scientific_notation_if_needed
def make_format_spec(num, align=">"):
"""
@@ -83,25 +86,30 @@ def format_text(
):
"""
Format a text string for canvas to display according to
input key value pair and make proper aligment
For invalid value, it displays N/A
All strings to be displayed on Canvas need to use this method
input key-value pair and make proper alignment.
Uses scientific notation formatting when needed.
For invalid value, it displays N/A.
"""
# Step 1: Build format spec using make_format_spec
value_format = make_format_spec(value_step_prec_rightalign, value_align)
if is_value_valid(value):
value_str = "{val:{format}}".format(val=value, format=value_format)
# Step 2: Extract width and precision as integer
match = re.match(r"([<>=^])(\d+)(?:\.(\d+))?([a-zA-Z])?", value_format)
if match:
align_char = match.group(1)
width_align = int(match.group(2))
precision_digits = match.group(3)
fmt_type_align = match.group(4) or "f"
precision = int(precision_digits) if precision_digits else 0
else:
import re
match = re.search(r"[<>=^](\d+)", value_format)
width = int(match.group(1)) if match else 6
# Use same alignment as in value_format (first char)
align = value_format[0]
value_str = f"{'N/A':{align}{width}}"
# Fallback to default values
align_char = value_align
width_align = 6
precision = 2
fmt_type_align = "f"
# Step 3: Format the key using make_format_spec
key_format = (
make_format_spec(key_step_prec_leftalign, key_align)
if key is not None
@@ -109,19 +117,36 @@ def format_text(
)
key_str = (
"{key:{key_format}}".format(key=key, key_format=key_format)
if key and isinstance(key, (int, float))
if key is not None and isinstance(key, (int, float))
else str(key)
if key
if key is not None
else None
)
unit_string = post_description_with_space if not "N/A" in value_str else ""
# Step 4: Format the value or fallback to N/A
if is_value_valid(value):
formatted_value = format_scientific_notation_if_needed(
value,
align=align_char,
width_align=width_align,
precision=precision,
fmt_type_align=fmt_type_align,
max_length=width_align,
sci_lower_bound=1e-3,
sci_upper_bound=1e3,
)
value_str = formatted_value
else:
value_str = f"{'N/A':{align_char}{width_align}}"
# Step 5: Unit and Final Output
unit_string = post_description_with_space if "N/A" not in value_str else ""
if key_str is not None:
result_str_no_unit = f"{key_str}{mark_between}{value_str}"
else:
result_str_no_unit = value_str
result_str_no_unit = (
"{key}{mark}{value}".format(key=key_str, value=value_str, mark=mark_between)
if key is not None
else "{value}".format(value=value_str)
)
result_str = result_str_no_unit + unit_string
return result_str
@@ -603,7 +628,7 @@ class ScalarL1DCache(RectFrame):
key="Hit",
value=self.hit,
key_step_prec_leftalign=6,
value_step_prec_rightalign=6,
value_step_prec_rightalign=6.0,
post_description_with_space=" %",
),
)
@@ -38,7 +38,7 @@ import tempfile
import time
import uuid
from pathlib import Path as path
from typing import Optional
from typing import Optional, Union
import pandas as pd
import yaml
@@ -1645,3 +1645,78 @@ def parse_sets_yaml(arch):
def get_uuid(length=8):
return uuid.uuid4().hex[:length]
def format_scientific_notation_if_needed(
value: Union[int, float],
align: str = ">",
width_align: int = 6,
precision: int = 2,
fmt_type_align: str = "f",
max_length: int = 6,
sci_lower_bound: float = 1e-2,
sci_upper_bound: float = 1e6,
) -> str:
"""
Format a numeric value as normal or scientific notation string.
Uses scientific notation if:
- abs(value) < sci_lower_bound (but not zero)
- abs(value) >= sci_upper_bound
- formatted normal string length exceeds max_length
Parameters:
- value: numeric value to format
- align: alignment character ('<', '>', '^', '=')
- width_align: total width of formatted output
- precision: number of digits after decimal point
- fmt_type_align: format type, e.g., 'f', 'e', 'g'
- max_length: max allowed length for normal format string (excluding padding)
- sci_lower_bound: lower bound for scientific notation usage
- sci_upper_bound: upper bound for scientific notation usage
Returns:
- formatted string according to the criteria, respecting alignment
"""
abs_val = abs(value)
use_sci = False
# Build format specifiers
normal_format_spec = f"{align}{width_align}.{precision}{fmt_type_align}"
sci_format_spec = f"{align}{width_align}.{precision}e"
normal_str = None # will hold formatted normal string (with padding)
sci_str = None # will hold formatted scientific string (with padding)
if abs_val != 0:
if abs_val < sci_lower_bound or abs_val >= sci_upper_bound:
use_sci = True
else:
try:
normal_str = format(value, normal_format_spec)
normal_str_strip = normal_str.strip()
sci_str = format(value, sci_format_spec)
sci_str_strip = sci_str.strip()
# Decide based on length of stripped strings (ignore padding)
if (
len(normal_str_strip) > len(sci_str_strip)
or len(normal_str_strip) > max_length
):
use_sci = True
except Exception:
# Fallback to scientific if formatting fails
use_sci = True
if use_sci:
if sci_str is None:
sci_str = format(value, sci_format_spec)
formatted = sci_str
else:
if normal_str is None:
normal_str = format(value, normal_format_spec)
formatted = normal_str
return formatted
@@ -9290,3 +9290,72 @@ def test_set_parser():
assert "compute_thruput_util" in result
assert result["compute_thruput_util"]["title"] == "Compute Throughput Utilization"
@pytest.mark.sci_notion
def test_scientific_notation_trigger_below_lower_bound():
value = 0.0001
result = utils.format_scientific_notation_if_needed(value)
assert pytest.approx(float(result.strip()), rel=1e-9) == value
@pytest.mark.sci_notion
def test_scientific_notation_trigger_at_lower_bound():
value = 0.01
result = utils.format_scientific_notation_if_needed(value)
assert pytest.approx(float(result.strip()), rel=1e-9) == value
def test_scientific_notation_trigger_above_upper_bound():
value = 1234567890
result = utils.format_scientific_notation_if_needed(value)
assert pytest.approx(float(result.strip()), rel=1e-9) == value
@pytest.mark.sci_notion
def test_scientific_notation_trigger_just_below_upper_bound():
value = 999999
result = utils.format_scientific_notation_if_needed(value, precision=6)
assert pytest.approx(float(result.strip()), rel=1e-6) == value
@pytest.mark.sci_notion
def test_scientific_notation_trigger_zero():
value = 0
result = utils.format_scientific_notation_if_needed(value)
assert float(result.strip()) == value # Exact match for zero
@pytest.mark.sci_notion
def test_scientific_notation_trigger_slightly_below_lower_bound():
value = 0.009
result = utils.format_scientific_notation_if_needed(value)
assert pytest.approx(float(result.strip()), rel=1e-9) == value
@pytest.mark.sci_notion
def test_scientific_notation_trigger_well_below_lower_bound():
value = 1e-5
result = utils.format_scientific_notation_if_needed(value)
assert pytest.approx(float(result.strip()), rel=1e-9) == value
@pytest.mark.sci_notion
def test_scientific_notation_trigger_well_above_upper_bound():
value = 1e10
result = utils.format_scientific_notation_if_needed(value)
assert pytest.approx(float(result.strip()), rel=1e-9) == value
@pytest.mark.sci_notion
def test_alignment_and_width():
value = 1e10
result = utils.format_scientific_notation_if_needed(
value,
align=">",
width_align=12,
precision=2,
fmt_type_align="f",
max_length=8,
)
assert pytest.approx(float(result.strip()), rel=1e-9) == value