已合并
expf sqrtf接口支持subnormal #4722
expf sqrtf接口支持subnormal #4722
已合并
xuyiyang创建于 7月29日
共 4 个文件变更+295-27
@@ -29,6 +29,8 @@
29#if (__NPU_ARCH__ == 3510) || (__NPU_ARCH__ == 5102)29#if (__NPU_ARCH__ == 3510) || (__NPU_ARCH__ == 5102)
30 30 
31#define ASCRT_FOUR_BYTE_LEN_U 32U31#define ASCRT_FOUR_BYTE_LEN_U 32U
32+constexpr float __internal_subnormal_boundary =
33+ 1.17549435e-38f; // 1.17549435e-38f: subnormal floating-point number boundary
32 34 
33__SIMT_DEVICE_FUNCTIONS_DECL__ inline long int lroundf(float x)35__SIMT_DEVICE_FUNCTIONS_DECL__ inline long int lroundf(float x)
34{36{
@@ -75,7 +77,103 @@ __SIMT_DEVICE_FUNCTIONS_DECL__ inline float fabsf(float x) { return __fabsf(x);
75 77 
76__SIMT_DEVICE_FUNCTIONS_DECL__ inline float fmaf(float x, float y, float z) { return __fma(x, y, z); }78__SIMT_DEVICE_FUNCTIONS_DECL__ inline float fmaf(float x, float y, float z) { return __fma(x, y, z); }
77 79 
78-__SIMT_DEVICE_FUNCTIONS_DECL__ inline float expf(float x) { return __expf(x); }80+/*
81+ * Computes a refined sqrt approximation for float inputs.
82+ *
83+ * calculation to move subnormal or very small values into a more stable
84+ * range. The result is scaled back by 2^-12 because sqrt(x * 2^24)
85+ * equals sqrt(x) * 2^12.
86+ *
87+ * then applies one Newton-style correction to inv:
88+ * err0 = 1 - b * inv * inv
89+ * inv = inv + err0 * inv * 0.5
90+ *
91+ * The final sqrt value is reconstructed and corrected:
92+ * y = inv * b
93+ * err1 = b - y * y
94+ * y = y + 0.5 * inv * err1
95+ *
96+ * @param x The input value.
97+ * @return The refined square root approximation.
98+ */
99+__SIMT_DEVICE_FUNCTIONS_DECL__ inline float __internal_sqrtf_precise_impl(float x)
100+{
101+ constexpr float scale_up = 16777216.0f; // 2^24
102+ constexpr float scale_down = 0.000244140625f; // 2^-12
103+ constexpr float half = 0.5f;
104+ 
105+ // Return early for zero and positive infinity.
L
Llouyuxuan8月17日

级别:一般 问题:__internal_sqrtf_precise_impl 仅对 0 和 +Inf 提前返回,未显式处理负数与 NaN;负数会进入 x<1.0f 缩放分支,结果依赖 __sqrtf(负数) 的底层行为 影响:若硬件 __sqrtf 对负数不返回 NaN,后续 fmaf 精化会输出看似合法的有限垃圾值而非 NaN,违反 IEEE754 且错误难以察觉 修复建议:在缩放前增加负数与 NaN 守卫(如 if (x < 0.0f) return NaN),并补充负数/NaN 测试用例

likedislike
106+ if (x == 0.0f || x == ASCRT_INF_F) {
107+ return x;
108+ }
109+ 
110+ // Scale inputs smaller than 1 to the normal range.
111+ bool scaled = x < 1.0f;
112+ float b = scaled ? x * scale_up : x;
113+ 
114+ // Compute an approximate reciprocal square root.
115+ float sqrt_b = __sqrtf(b);
116+ float inv = 1.0f / sqrt_b;
117+ 
118+ // Refine the reciprocal square root.
119+ float err0 = fmaf(-(b * inv), inv, 1.0f);
120+ inv = fmaf(err0, inv * half, inv);
121+ 
122+ // Convert to a square-root approximation, then compensate the sqrt residual.
123+ float y = inv * b;
124+ float err1 = fmaf(-y, y, b);
125+ y = fmaf(inv * half, err1, y);
126+ 
127+ // Scale the result back if the input was multiplied by 2^24.
128+ return scaled ? y * scale_down : y;
129+}
130+ 
131+#ifdef __ASC_FTZ__
132+__SIMT_DEVICE_FUNCTIONS_DECL__ inline float __internal_expf(float x) { return __expf(x); }
133+ 
134+#ifdef __ASC_PREC_SQRT__
135+__SIMT_DEVICE_FUNCTIONS_DECL__ inline float __internal_sqrtf(float x)
136+{
137+ // Treat positive subnormal inputs as 0 before entering the precise sqrt path.
138+ if (x > 0.0f && x < __internal_subnormal_boundary) {
139+ x = 0.0f;
140+ }
141+ return __internal_sqrtf_precise_impl(x);
142+}
143+#else
144+__SIMT_DEVICE_FUNCTIONS_DECL__ inline float __internal_sqrtf(float x) { return __sqrtf(x); }
145+#endif
146+#endif
147+ 
148+#ifndef __ASC_FTZ__
149+__SIMT_DEVICE_FUNCTIONS_DECL__ inline float __internal_expf(float x)
150+{
151+ constexpr float __internal_subnormal_input_boundary =
152+ -87.3365478515625f; // Input range where expf returns a subnormal result
153+ if (x <= __internal_subnormal_input_boundary) {
154+ float half_y = __expf(x * 0.5f);
155+ return half_y * half_y;
156+ }
157+ return __expf(x);
158+}
159+ 
160+#ifdef __ASC_PREC_SQRT__
161+__SIMT_DEVICE_FUNCTIONS_DECL__ inline float __internal_sqrtf(float x) { return __internal_sqrtf_precise_impl(x); }
162+#else
163+__SIMT_DEVICE_FUNCTIONS_DECL__ inline float __internal_sqrtf(float x)
164+{
165+ constexpr float scale_up = 16777216.0f; // 2^24
166+ constexpr float scale_down = 0.000244140625f; // 2^-12
167+ // Scale positive subnormal inputs to the normal range, then scale the result back.
168+ if (x < __internal_subnormal_boundary) {
169+ return __sqrtf(x * scale_up) * scale_down;
170+ }
171+ return __sqrtf(x);
172+}
173+#endif
174+#endif
175+ 
176+__SIMT_DEVICE_FUNCTIONS_DECL__ inline float expf(float x) { return __internal_expf(x); }
79 177 
80__SIMT_DEVICE_FUNCTIONS_DECL__ inline float logf(float x)178__SIMT_DEVICE_FUNCTIONS_DECL__ inline float logf(float x)
81{179{
@@ -87,7 +185,7 @@ __SIMT_DEVICE_FUNCTIONS_DECL__ inline float logf(float x)
87 185 
88__SIMT_DEVICE_FUNCTIONS_DECL__ inline float log2f(float x) { return logf(x) / logf(2.0f); }186__SIMT_DEVICE_FUNCTIONS_DECL__ inline float log2f(float x) { return logf(x) / logf(2.0f); }
89 187 
90-__SIMT_DEVICE_FUNCTIONS_DECL__ inline float sqrtf(float x) { return __sqrtf(x); }188+__SIMT_DEVICE_FUNCTIONS_DECL__ inline float sqrtf(float x) { return __internal_sqrtf(x); }
L
Llouyuxuan8月17日

级别:提示 问题:非 FTZ 模式下 sqrtf 对次正规输入的返回值由可能的 0(原 __sqrtf 冲刷)变为正确的正规数结果,rsqrtf 对次正规输入也可能由 inf 变为有限大数 影响:下游依赖旧行为(如以 0 或 inf 判断下溢)的算子数值结果会变化,属预期修复但存在隐性依赖风险 修复建议:排查调用 sqrtf/rsqrtf 的现有算子在次正规输入下的行为变化,确认符合预期并同步更新相关精度基线

likedislike
91 189 
92__SIMT_DEVICE_FUNCTIONS_DECL__ inline float rsqrtf(float x) { return 1.0f / sqrtf(x); }190__SIMT_DEVICE_FUNCTIONS_DECL__ inline float rsqrtf(float x) { return 1.0f / sqrtf(x); }
93 191 
@@ -28,6 +28,13 @@ set(ASCENDC_TEST_HEADER_FILES
28 ${ASCENDC_DIR}/impl/simt_api28 ${ASCENDC_DIR}/impl/simt_api
29)29)
30 30 
31+file(GLOB ASCENDC_TEST_SIMT_COMMON_SRC_FILES
32+ common_simt/*.cpp
33+)
34+set(ASCENDC_TEST_ascend950pr_9599_SIMT_SUBNORMAL_CASE_SRC_FILE
35+ ${CMAKE_CURRENT_SOURCE_DIR}/ascendc_case_ascend950pr_9599_simt/test_operator_simt_subnormal.cpp
36+)
37+ 
31# # ================ simt_api kernel ut ================38# # ================ simt_api kernel ut ================
32# ascend950pr_9599 simt test cases39# ascend950pr_9599 simt test cases
33file(GLOB ASCENDC_TEST_ascend950pr_9599_SIMT_CASE_SRC_FILES40file(GLOB ASCENDC_TEST_ascend950pr_9599_SIMT_CASE_SRC_FILES
@@ -41,43 +48,47 @@ file(GLOB ASCENDC_TEST_mc62cm12aa_SIMT_CASE_SRC_FILES
41 ascendc_case_mc62cm12aa_simt/*.cpp48 ascendc_case_mc62cm12aa_simt/*.cpp
42)49)
43 50 
51+add_library(simt_api_test_common INTERFACE)
52+target_compile_definitions(simt_api_test_common INTERFACE
53+ UT_TEST
54+ ASCENDC_OOM=1
55+ ASCENDC_DUMP=0
56+ __CCE_KT_TEST__=1
57+ ASCENDC_CPU_DEBUG=1
58+ ASCENDC_DEBUG=1
59+)
60+target_include_directories(simt_api_test_common INTERFACE
61+ ${ASCENDC_TEST_HEADER_FILES}
62+ ${ASCEND_CANN_PACKAGE_PATH}/tools/tikicpulib/lib/include
63+ ${ASCEND_CANN_PACKAGE_PATH}/include/base/
64+ ${ASCEND_CANN_PACKAGE_PATH}/include/metadef/
65+)
66+target_compile_options(simt_api_test_common INTERFACE
67+ -fno-access-control
68+)
69+target_link_libraries(simt_api_test_common INTERFACE
70+ $<BUILD_INTERFACE:intf_llt_pub_basic>
71+ kernel_tiling
72+ -Wl,--no-as-needed
73+ error_manager
74+ mmpa
75+ c_sec
76+ -Wl,--as-needed
77+)
78+ 
44foreach(product_type ${SIMT_PRODUCT_TYPE_LIST})79foreach(product_type ${SIMT_PRODUCT_TYPE_LIST})
45 add_executable(ascendc_ut_simt_api_${product_type}80 add_executable(ascendc_ut_simt_api_${product_type}
46 ${ASCENDC_DIR}/tests/main.cpp81 ${ASCENDC_DIR}/tests/main.cpp
47- ../common/k3_pvwrap.cpp
48 ${ASCENDC_TEST_${product_type}_SIMT_CASE_SRC_FILES}82 ${ASCENDC_TEST_${product_type}_SIMT_CASE_SRC_FILES}
49 )83 )
50 84 
51 target_compile_definitions(ascendc_ut_simt_api_${product_type} PRIVATE85 target_compile_definitions(ascendc_ut_simt_api_${product_type} PRIVATE
52- UT_TEST
53- ASCENDC_OOM=1
54- ASCENDC_DUMP=0
55- __CCE_KT_TEST__=1
56- ASCENDC_CPU_DEBUG=1
57- ASCENDC_DEBUG=1
58 $<$<STREQUAL:${product_type},ascend950pr_9599>:__NPU_ARCH__=3510;__DAV_C310__;__DAV_C310_VEC__>86 $<$<STREQUAL:${product_type},ascend950pr_9599>:__NPU_ARCH__=3510;__DAV_C310__;__DAV_C310_VEC__>
59 $<$<STREQUAL:${product_type},mc62cm12aa>:__NPU_ARCH__=5102>87 $<$<STREQUAL:${product_type},mc62cm12aa>:__NPU_ARCH__=5102>
60 )88 )
61 89 
62- target_include_directories(ascendc_ut_simt_api_${product_type} PRIVATE
63- ${ASCENDC_TEST_HEADER_FILES}
64- ${ASCEND_CANN_PACKAGE_PATH}/tools/tikicpulib/lib/include
65- ${ASCEND_CANN_PACKAGE_PATH}/include/base/
66- ${ASCEND_CANN_PACKAGE_PATH}/include/metadef/
67- )
68- 
69- target_compile_options(ascendc_ut_simt_api_${product_type} PRIVATE
70- -fno-access-control
71- )
72- 
73 target_link_libraries(ascendc_ut_simt_api_${product_type} PRIVATE90 target_link_libraries(ascendc_ut_simt_api_${product_type} PRIVATE
74- $<BUILD_INTERFACE:intf_llt_pub_basic>91+ simt_api_test_common
75- kernel_tiling
76- -Wl,--no-as-needed
77- error_manager
78- mmpa
79- c_sec
80- -Wl,--as-needed
81 )92 )
82 93 
83 run_llt_test(94 run_llt_test(
@@ -92,4 +103,40 @@ foreach(product_type ${SIMT_PRODUCT_TYPE_LIST})
92 add_dependencies(ascendc_ut_simt_api_${product_type} math_bfloat16)103 add_dependencies(ascendc_ut_simt_api_${product_type} math_bfloat16)
93 add_subdirectory(math_cpp)104 add_subdirectory(math_cpp)
94 add_dependencies(ascendc_ut_simt_api_${product_type} math_cpp)105 add_dependencies(ascendc_ut_simt_api_${product_type} math_cpp)
106+ 
107+ if("${product_type}" STREQUAL "ascend950pr_9599")
108+ set(SIMT_SUBNORMAL_TARGETS
109+ ascendc_ut_simt_subnormal_ftz_false_prec_false
110+ ascendc_ut_simt_subnormal_ftz_false_prec_true
111+ ascendc_ut_simt_subnormal_ftz_true_prec_false
112+ ascendc_ut_simt_subnormal_ftz_true_prec_true
113+ )
114+ 
115+ foreach(target_name ${SIMT_SUBNORMAL_TARGETS})
116+ add_executable(${target_name}
117+ ${ASCENDC_DIR}/tests/main.cpp
118+ ${ASCENDC_TEST_SIMT_COMMON_SRC_FILES}
119+ ${ASCENDC_TEST_ascend950pr_9599_SIMT_SUBNORMAL_CASE_SRC_FILE}
120+ )
121+ 
122+ target_link_libraries(${target_name} PRIVATE simt_api_test_common)
123+ target_compile_definitions(${target_name} PRIVATE
124+ __NPU_ARCH__=3510
125+ __DAV_C310__
126+ __DAV_C310_VEC__
127+ )
128+ 
129+ if("${target_name}" MATCHES "_ftz_true_")
130+ target_compile_definitions(${target_name} PRIVATE __ASC_FTZ__)
131+ endif()
132+ if("${target_name}" MATCHES "_prec_true$")
133+ target_compile_definitions(${target_name} PRIVATE __ASC_PREC_SQRT__)
134+ endif()
135+ 
136+ run_llt_test(
137+ TARGET ${target_name}
138+ TASK_NUM 1
139+ )
140+ endforeach()
141+ endif()
95endforeach()142endforeach()
@@ -0,0 +1,115 @@
1+/**
2+ * Copyright (c) 2026 Huawei Technologies Co., Ltd.
3+ * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4+ * CANN Open Software License Agreement Version 2.0 (the "License").
5+ * Please refer to the License for details. You may not use this file except in compliance with the License.
6+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7+ * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8+ * See LICENSE in the root of the software repository for the full text of the License.
9+ */
10+ 
11+#include <gtest/gtest.h>
12+#include <cmath>
13+#include <limits>
14+#include <type_traits>
15+#include "simt_compiler_stub.h"
16+#include "kernel_operator.h"
17+#include "simt_api/math_functions.h"
18+ 
19+class FloatSubnormalTestsuite : public testing::Test {
20+protected:
21+ void SetUp() {}
22+ void TearDown() {}
23+};
24+ 
25+TEST_F(FloatSubnormalTestsuite, ExpFloat)
26+{
27+ float a = 1.17549421e-38f;
28+ float x = 1.0f;
L
Llouyuxuan8月17日
已过期

级别:严重 问题:用例仅使用 1.0f/2.0f 等正规数输入,未覆盖任何次正规数及边界值(如 1.17549435e-38f 两侧、FLT_TRUE_MIN≈1.4e-45f、expf 下溢区 x≈-87.3、sqrtf 输入 1e-40f),也未测 0/Inf/NaN/负数 影响:本 PR 的核心行为变更(次正规数输入/输出处理)未被验证,该部分回归无法被检出 修复建议:补充次正规输入、边界常数两侧、下溢输入、0/±Inf/NaN/负数等用例,并覆盖 FTZ 与 ASC_PREC_SQRT 的四种开关组合

likedislike
29+ float y = -80.0f;
30+ float subnormalOutputInput = -90.0f;
31+ float z = 0.0f;
32+ EXPECT_FLOAT_EQ(std::exp(x), expf(x));
33+ EXPECT_FLOAT_EQ(std::exp(a), expf(a));
34+ EXPECT_FLOAT_EQ(std::exp(y), expf(y));
35+#ifdef __ASC_FTZ__
36+ EXPECT_FLOAT_EQ(std::exp(subnormalOutputInput), expf(subnormalOutputInput));
37+#else
38+ EXPECT_NEAR(std::exp(subnormalOutputInput), expf(subnormalOutputInput), 1e-43f);
39+#endif
40+ EXPECT_FLOAT_EQ(std::exp(z), expf(z));
41+}
42+ 
43+TEST_F(FloatSubnormalTestsuite, InternalExpFloat)
44+{
45+ volatile float normalInput = -80.0f;
46+ volatile float subnormalOutputInput = -90.0f;
47+ EXPECT_FLOAT_EQ(std::exp(static_cast<float>(normalInput)), __internal_expf(normalInput));
48+#ifdef __ASC_FTZ__
49+ EXPECT_FLOAT_EQ(std::exp(static_cast<float>(subnormalOutputInput)), __internal_expf(subnormalOutputInput));
50+#else
51+ EXPECT_NEAR(std::exp(static_cast<float>(subnormalOutputInput)), __internal_expf(subnormalOutputInput), 1e-43f);
52+#endif
53+}
54+ 
55+TEST_F(FloatSubnormalTestsuite, SqrtFloat)
56+{
57+ float x = 1.0f;
58+ float y = 1.17549421e-38f;
59+ float z = 0.0f;
60+ float normal = 4.0f;
61+ EXPECT_FLOAT_EQ(std::sqrt(x), sqrtf(x));
62+#ifdef __ASC_FTZ__
63+ EXPECT_FLOAT_EQ(std::sqrt(y), sqrtf(y));
64+#else
65+ EXPECT_NEAR(std::sqrt(y), sqrtf(y), 1e-24f);
66+#endif
67+ EXPECT_FLOAT_EQ(std::sqrt(z), sqrtf(z));
68+#ifdef __ASC_PREC_SQRT__
69+ float scaledNormal = 0.25f;
70+ EXPECT_NEAR(std::sqrt(scaledNormal), sqrtf(scaledNormal), 1e-6f);
71+ EXPECT_NEAR(std::sqrt(normal), sqrtf(normal), 1e-6f);
72+ EXPECT_TRUE(std::isinf(sqrtf(ASCRT_INF_F)));
73+#else
74+ EXPECT_FLOAT_EQ(std::sqrt(normal), sqrtf(normal));
75+#endif
76+}
77+ 
78+TEST_F(FloatSubnormalTestsuite, InternalSqrtFloat)
79+{
80+ volatile float subnormal = std::numeric_limits<float>::denorm_min();
81+ volatile float normal = 4.0f;
82+#ifdef __ASC_PREC_SQRT__
83+ volatile float zero = 0.0f;
84+ volatile float inf = ASCRT_INF_F;
85+ volatile float scaledNormal = 0.25f;
86+ EXPECT_FLOAT_EQ(0.0f, __internal_sqrtf(zero));
87+ EXPECT_TRUE(std::isinf(__internal_sqrtf(inf)));
88+#ifdef __ASC_FTZ__
89+ EXPECT_FLOAT_EQ(std::sqrt(static_cast<float>(subnormal)), __internal_sqrtf(subnormal));
90+#else
91+ EXPECT_NEAR(std::sqrt(static_cast<float>(subnormal)), __internal_sqrtf(subnormal), 1e-24f);
92+#endif
93+ EXPECT_NEAR(std::sqrt(static_cast<float>(scaledNormal)), __internal_sqrtf(scaledNormal), 1e-6f);
94+ EXPECT_NEAR(std::sqrt(static_cast<float>(normal)), __internal_sqrtf(normal), 1e-6f);
95+#else
96+#ifdef __ASC_FTZ__
97+ EXPECT_FLOAT_EQ(std::sqrt(static_cast<float>(subnormal)), __internal_sqrtf(subnormal));
98+#else
99+ EXPECT_NEAR(std::sqrt(static_cast<float>(subnormal)), __internal_sqrtf(subnormal), 1e-24f);
100+#endif
101+ EXPECT_FLOAT_EQ(std::sqrt(static_cast<float>(normal)), __internal_sqrtf(normal));
102+#endif
103+}
104+ 
105+TEST_F(FloatSubnormalTestsuite, InternalPreciseSqrtFloatCommon)
106+{
107+ volatile float zero = 0.0f;
108+ volatile float inf = ASCRT_INF_F;
109+ volatile float scaled = 0.25f;
110+ volatile float normal = 4.0f;
111+ EXPECT_FLOAT_EQ(0.0f, __internal_sqrtf_precise_impl(zero));
112+ EXPECT_TRUE(std::isinf(__internal_sqrtf_precise_impl(inf)));
113+ EXPECT_NEAR(std::sqrt(static_cast<float>(scaled)), __internal_sqrtf_precise_impl(scaled), 1e-6f);
114+ EXPECT_NEAR(std::sqrt(static_cast<float>(normal)), __internal_sqrtf_precise_impl(normal), 1e-6f);
115+}
@@ -117,6 +117,10 @@ basic_test_five_targets=(
117 ascendc_run_all_tensor_api_header_checks_npu117 ascendc_run_all_tensor_api_header_checks_npu
118 ascendc_run_all_tensor_api_header_checks_npu_single118 ascendc_run_all_tensor_api_header_checks_npu_single
119 ascendc_ut_simt_api_ascend950pr_9599119 ascendc_ut_simt_api_ascend950pr_9599
120+ ascendc_ut_simt_subnormal_ftz_false_prec_false
121+ ascendc_ut_simt_subnormal_ftz_false_prec_true
122+ ascendc_ut_simt_subnormal_ftz_true_prec_false
123+ ascendc_ut_simt_subnormal_ftz_true_prec_true
120)124)
121 125 
122arm_test_targets=(126arm_test_targets=(
@@ -124,6 +128,10 @@ arm_test_targets=(
124 ascendc_ut_elf_tool128 ascendc_ut_elf_tool
125 ascendc_ut_pack_kernel129 ascendc_ut_pack_kernel
126 ascendc_ut_simt_api_ascend950pr_9599130 ascendc_ut_simt_api_ascend950pr_9599
131+ ascendc_ut_simt_subnormal_ftz_false_prec_false
132+ ascendc_ut_simt_subnormal_ftz_false_prec_true
133+ ascendc_ut_simt_subnormal_ftz_true_prec_false
134+ ascendc_ut_simt_subnormal_ftz_true_prec_true
127 ascendc_run_all_host_header_checks135 ascendc_run_all_host_header_checks
128 ascendc_run_all_host_header_checks_npu136 ascendc_run_all_host_header_checks_npu
129 ascendc_run_all_host_header_checks_npu_single137 ascendc_run_all_host_header_checks_npu_single