From patchwork Thu Aug 1 21:32:31 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jonathan Wakely X-Patchwork-Id: 1968053 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=Yix7IMBK; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=2620:52:3:1:0:246e:9693:128c; helo=server2.sourceware.org; envelope-from=gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=patchwork.ozlabs.org) Received: from server2.sourceware.org (server2.sourceware.org [IPv6:2620:52:3:1:0:246e:9693:128c]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4WZj8Y70myz1yZl for ; Fri, 2 Aug 2024 07:41:33 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 2AF9D3858D26 for ; Thu, 1 Aug 2024 21:41:32 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTP id BBEDF3858C39 for ; Thu, 1 Aug 2024 21:39:46 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org BBEDF3858C39 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org BBEDF3858C39 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1722548388; cv=none; b=exznmzGydYYAd0P7WwWOFPJatBg1vNhviYtuHrFTWfJKuqcSCSySKhVxahSlDWSbnqzrFPNT1mGKdsdtCnUXrL7m2nh2OqYJmTqrJBazNWm2IoZjYw+hvyrHqLuHu13U3l/qc5r4aLQFh3dbv1HBaaDHFc+P070eEW2T3U78mEY= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1722548388; c=relaxed/simple; bh=hf3oXjxm8CZaQnoGtwLxWLutzu2XmpsAuJX+Dq6YoXg=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=Mq1v65c9MtklE5KIMEQz9g5cREJ5iB3gXIIWrkck4yoz52gjs3hYi4ElA2zsb/CdvL6lFFnZ7L/5tz0jTGpBFhXxs5bGxfLguCqytJIF8j0RQoJ+eG1OdJyyNul8+CbHQqVfthu8D3DZHktFU1cJnuFmz5kJlIhLgTm7i2Ew+K4= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1722548386; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=Mt1E5Cbh+4R/4PlLdwFPuwy9bdoe8ZFjeseeesNzH2c=; b=Yix7IMBKrpbNBrHhJ3h4opZLa7ubyAV82D3rdnAUm9796+vKBJM9yHlKNURNdYODP0CbOn lD/ZbrVFLkkjZ0vmKleaT3Vvxw3393QT7IY5Y4epFHLQO5FqnV6W0vkZXg8fULs4G9bvev GLMLUzeXPHYAu66zC404Fh5juPIaB1U= Received: from mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-650-p6w4OjtcNxWPpxQ0LUhOCw-1; Thu, 01 Aug 2024 17:39:43 -0400 X-MC-Unique: p6w4OjtcNxWPpxQ0LUhOCw-1 Received: from mx-prod-int-02.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-02.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.15]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 4AD221955D42; Thu, 1 Aug 2024 21:39:42 +0000 (UTC) Received: from localhost (unknown [10.42.28.21]) by mx-prod-int-02.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 7F9901955D42; Thu, 1 Aug 2024 21:39:41 +0000 (UTC) From: Jonathan Wakely To: libstdc++@gcc.gnu.org, gcc-patches@gcc.gnu.org Cc: Andrew Waterman Subject: [PATCH] libstdc++: Preserve signbit of nan when converting float to double [PR113578] Date: Thu, 1 Aug 2024 22:32:31 +0100 Message-ID: <20240801213927.388966-6-jwakely@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.0 on 10.30.177.15 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-12.1 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H4, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org It makes me a big sad that we need to do this, but the _S_cast_flt function should get optimized away completely on targets where it doesn't need to do anything special. As far as I know only RISC-V needs special handling, but we could extend it to other targets if needed. Tested x86_64-linux. -- >8 -- LWG 117 specifies that inserting a float into an ostream should cast it to double, because there's no std::num_put::put member that takes a float. However, on RISC-V converting a NaN float to double loses the sign, which means that negative NaN floats are printed as positive. This has been reported as LWG 4101 and there is good support for fixing the standard to preserve the sign bit when printing negative NaN values. This change uses copysign((double)f, (double)std::bit_cast(f)) to get a double that preserves the sign. The inserters for extended floating-point types need the same treatment, so add a new _S_cast_flt helper to do the signbit-preserving conversion generically. So far only RISC-V has been confirmed to need this treatment, but we might need to extend it to other targets later. libstdc++-v3/ChangeLog: PR libstdc++/113578 * include/std/ostream (_S_cast_flt): New static member function to restore signbit after casting to double or long double. (operator<<(float), operator<<(_Float16), operator<<(_Float32)) (operator<<(_Float64), operator(_Float128)) (operator<<(__bfloat16_t)): Use _S_cast_flt. testsuite/27_io/basic_ostream/inserters_arithmetic/lwg4101.cc: New test. Co-authored-by: Andrew Waterman --- libstdc++-v3/include/std/ostream | 40 ++++++++++++++++--- .../inserters_arithmetic/lwg4101.cc | 14 +++++++ 2 files changed, 48 insertions(+), 6 deletions(-) create mode 100644 libstdc++-v3/testsuite/27_io/basic_ostream/inserters_arithmetic/lwg4101.cc diff --git a/libstdc++-v3/include/std/ostream b/libstdc++-v3/include/std/ostream index 12be6c4fd17..61d29a83691 100644 --- a/libstdc++-v3/include/std/ostream +++ b/libstdc++-v3/include/std/ostream @@ -233,7 +233,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { // _GLIBCXX_RESOLVE_LIB_DEFECTS // 117. basic_ostream uses nonexistent num_put member functions. - return _M_insert(static_cast(__f)); + return _M_insert(_S_cast_flt(__f)); } __ostream_type& @@ -246,7 +246,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __ostream_type& operator<<(_Float16 __f) { - return _M_insert(static_cast(__f)); + return _M_insert(_S_cast_flt(__f)); } #endif @@ -255,7 +255,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __ostream_type& operator<<(_Float32 __f) { - return _M_insert(static_cast(__f)); + return _M_insert(_S_cast_flt(__f)); } #endif @@ -264,7 +264,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __ostream_type& operator<<(_Float64 __f) { - return _M_insert(static_cast(__f)); + return _M_insert(_S_cast_flt(__f)); } #endif @@ -273,7 +273,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __ostream_type& operator<<(_Float128 __f) { - return _M_insert(static_cast(__f)); + return _M_insert(_S_cast_flt(__f)); } #endif @@ -282,7 +282,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __ostream_type& operator<<(__gnu_cxx::__bfloat16_t __f) { - return _M_insert(static_cast(__f)); + return _M_insert(_S_cast_flt(__f)); } #endif @@ -473,6 +473,34 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _M_write(const char_type* __s, streamsize __n) { std::__ostream_insert(*this, __s, __n); } #endif + + template + static _To + _S_cast_flt(_From __f) + { + _To __d = static_cast<_To>(__f); + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 4101: LWG 117 loses the sign for negative NaN on some arches. +#if defined __riscv +#if __has_builtin(__builtin_bit_cast) + _To __sign; + if _GLIBCXX17_CONSTEXPR (sizeof(__f) == sizeof(short)) + __sign = static_cast<_To>(__builtin_bit_cast(short, __f)); + else if _GLIBCXX17_CONSTEXPR (sizeof(__f) == sizeof(int)) + __sign = static_cast<_To>(__builtin_bit_cast(int, __f)); + else if _GLIBCXX17_CONSTEXPR (sizeof(__f) == sizeof(long long)) + __sign = static_cast<_To>(__builtin_bit_cast(long long, __f)); + else +#endif + __sign = __builtin_signbit(__f) ? _To(-1.0) : _To(+1.0); + + if _GLIBCXX17_CONSTEXPR (__is_same(_To, double)) + __d = __builtin_copysign(__d, __sign); + else if _GLIBCXX17_CONSTEXPR (__is_same(_To, long double)) + __d = __builtin_copysignl(__d, __sign); +#endif + return __d; + } }; /** diff --git a/libstdc++-v3/testsuite/27_io/basic_ostream/inserters_arithmetic/lwg4101.cc b/libstdc++-v3/testsuite/27_io/basic_ostream/inserters_arithmetic/lwg4101.cc new file mode 100644 index 00000000000..1e1b8e08535 --- /dev/null +++ b/libstdc++-v3/testsuite/27_io/basic_ostream/inserters_arithmetic/lwg4101.cc @@ -0,0 +1,14 @@ +// { dg-do run } +// LWG 4101. LWG 117 loses the sign for negative NaN on some architectures + +#include +#include +#include + +int main() +{ + float nan = std::numeric_limits::quiet_NaN(); + std::ostringstream os; + os << -nan; + VERIFY( os.str()[0] == '-' ); +}